EnvironmentOps.java
/*
** Module : EnvironmentOps.java
** Abstract : Progress 4GL compatible environment related methods
**
** Copyright (c) 2005-2025, Golden Code Development Corporation.
**
** -#- -I- --Date-- --JPRM-- ---------------------------Description----------------------------
** 001 GES 20050628 @21564 Created initial version, supporting common
** global variables that are called built-in
** functions in Progress but which take no
** parameters, no empty parens. These also
** are read-only (they cannot be assigned).
** 002 GES 20050729 @21921 Added getSourceName() and modified the
** default propath.
** 003 ECF 20051214 @23753 Removed getCurrentDatabaseName(). This was a
** placeholder for P2J's DBNAME replacement
** implementation. This is now provided in the
** ConnectionManager class instead.
** 004 GES 20060123 @24034 Move to a directory based implementation for
** the Progress compatibility global vars.
** 005 GES 20060125 @24071 Shift to SourceNameMapper backed stack
** processing.
** 006 EVL 20060713 @28035 Changing the approach to get the source file
** name from index of the Progress procedure on
** the stack.
** 007 GES 20070321 @32506 Added support for assigning the PROPATH.
** 008 GES 20071214 @36369 Fix for NPE in getSourceName().
** 009 GES 20080123 @36902 Better propath and separator defaults.
** 010 GES 20080321 @37628 Added SESSION:BATCH-MODE support.
** 011 GES 20080414 @38014 Added SESSION:CHARSET support.
** 012 GES 20080924 @39940 Mask off inner classes from program-name
** processing.
** 013 GES 20090512 @42147 Minor optimization for program-name.
** 014 ECF 20101005 Added getTempDirectory method.
** 015 GES 20110713 Changed default 4GL version string to "10.2B".
** 016 CS 20121104 Implement PROPATH assignment.
** 017 EVL 20130108 Fixing the PROPATH handling. We have to substitute
** system path separator with comma when getting
** PROPATH value. Also the default path and file
** separators value is chaged to return the current
** OS dependent one from File.pathSeparator and
** File.separator. Also the getLegacyCaseSensitive()
** method has been changed to set the default value
** depending on the current OS.
** 018 EVL 20130128 Adding support for GET-KEY-VALUE, PUT-KEY-VALUE,
** USE, LOAD, UNLOAD statements. The COLOR and FONT
** options for PUT-KEY-VALUE is not yet supported.
** The corresponting Java methods are just stubs to
** be implemented later.
** 019 VMN 20130219 Added support for attributes
** SESSION:FIRST-BUFFER (KW_FIRST_BU)
** 020 CA 20130222 Removed getFirstBuffer (stub is in SessionUtils).
** 021 EVL 20130529 Changing the setSearchPath method to take into account the current
** PROPATH value while setting up the new one. The new item is adding
** in the head of the string replacing the variable part of the PROPATH
** value. Also the methods splitPath and fixupPropath were modified to
** consider the propath separator is not the same as OS dependent file
** separator. The PROPATH_SEPARATOR string constant has been added to
** centralize this value. The fixed part of the PROPATH value has been
** initialized only once from server ini file and used in every propath
** modification as additional appendix after comma separator. The new
** change is adding the one more member to the WorkArea to separate the
** fixed and changeable parts. The member propath has been removed from
** main class, this value is getting now dynamically.
** 022 CA 20130604 Added setter for the batch mode state.
** 023 EVL 20130625 Changing the handling of incoming load() call to work with
** environment. In case of only two character or String parameters in
** this call we have environment and base-key input options. Because
** the directory option is useless in this case - this option is valid
** only for INI file based environment but without base-key "INI" the
** load() call is referred to the registry related case. For registry
** on the other hand the directory option can not be applied. So two
** input parameters means only environment and base-key variant.
** 024 CA 20140218 PROGRAM-NAME is now implemented by ProcedureManager.getStackEntry,
** which is called by getSourceName().
** 025 EVL 20140325 Changing request the OS name, type and path separator to the client
** side detection.
** 026 CA 20140702 Added isCustomEnvironment and getEnvironmentName APIs.
** Added generic's. Removed unused WorkArea.pkgroot field.
** 027 CA 20140715 Fixed getTempDirectory - it needs to interrogate the P2J client.
** 028 MAG 20140822 Added getLegacyPlatform APIs.
** 029 MAG 20140908 Added COLOR-TABLE and FONT-TABLE notification.
** 030 MAG 20140912 INI files are available on all platforms regarding client type.
** 031 GES 20141030 Added CPINTERNAL.
** 032 SVL 20150114 For admin client use server-side OS version.
** 033 ECF 20150130 Added CPSTREAM, currently hard-coded to return UTF-8.
** 034 GES 20150615 PROGRAM-NAME() is updated to accept 64-bit integer values (this is
** valid in the 4GL.
** 035 ECF 20150715 Replace StringBuffer with StringBuilder.
** 036 IAS 20160331 Fixed potential NPE.
** 037 GES 20170228 Added javadoc.
** 038 GES 20171204 Improved comments and code formatting.
** 039 ECF 20180104 Added runtime support for converted RT-OPSYS global variable (FWD
** extension).
** 040 EVL 20180220 Now the current printer name is getting from client OS base.
** 041 CA 20180221 Added listeners to be notified when PROPATH has been set.
** 042 GES 20180322 Added getProcessArchitecture().
** 043 CA 20180313 Javadoc fix.
** 044 SBI 20180522 Renamed file-system container name into legacy-system and mapped
** legacyPlatform to legacy-system/opsys and removed legacyPlatform,
** added getLegacyAbsolutePathPrefixMap(DirectoryService).
** 045 SBI 20180531 Changed to use common Utils.getDirectoryNodeMap, added
** getLegacyEnvironmentVariables.
** 046 ECF 20180614 Performance improvement: cached frequently accessed configuration
** values to avoid expensive directory lookups.
** 047 CA 20180514 Added support for CURRENT-LANGUAGE setter.
** CA 20180523 Fixed environment name, when used in maps: registries are
** case-insensitive, stanza INI are case-sensitive.
** Fixed APIs when LOAD is used with mix of DIR, NEW and BASE-KEY.
** 048 SBI 20181207 Added getResoleFilePath() and getSupportedHelperApplications().
** 049 EVL 20190426 Rework for CPINTERNAL 4GL getter. Removed 'hack' to convert UTF-8 to
** ISO8859-1. Moved CHARSET, CPINTERNAL, CPSTREAM getters to I18nOps.
** 050 CA 20190529 Added load(character, boolean, character) signature.
** 051 EVL 20190730 The session:temp-directory should be finished with File.separator
** char unlike general OS temporary directory name.
** 052 GES 20191001 Added undocumented variants of proversion that take a parameter.
** Proper error handling, unknown handling and a default version of
** 11.6 were also added.
** 053 RFB 20200311 Added load(character, character, boolean, character) signature.
** 054 MP 20200324 Check if TEMP folder already have separator at the end.
** 055 RFB 20200427 Updated versionWorker() to cache version info in WorkArea, and added
** versionNumber(), isV9(), isV10(), isV11(), and is V12().
** RFB 20200428 Since we init versionNumber to 0, the worker needs to set the default.
** Made some improvements based upon code review.
** RFB 20200609 It is valid for USE to accept a null string. Our implementation was returning
** 4052 in that situation.
** CA 20210628 Added 'isLegacyPlatformWindows', to check if the legacy OS was Windows OS.
** AL2 20210630 Stop node editing if can't open batch when setting current-language.
** Reworked current-language getter and setter.
** HC 20211001 Implementation of i18n support.
** CA 20220514 The active session is now set for leaf sessions, too - this is required for a
** change in RemoteObject.obtainInstance, where first a check is done for an existing
** local proxy, and after that a network instance is obtained (a requirement for the
** server-side OS resources support, like memptr).
** SVL 20220620 Fixed USE. Support for USE("ininame") for the config file specified at the client
** startup.
** SVL 20220720 Fixed assignment of color tables to windows.
** EVL 20221013 Changed directory location and name for resolve-file-paths option.
** SBI 20221202 Reused new static method getCurrentLanguageString() and changed setCurrentLanguage
** to propagate language changes to the client.
** CA 20220514 Added setter for PROMSGS builtin variable.
** 056 TT 20230307 Implemented configurable directory override for the PROGRESS global variable
** 057 DDF 20230620 Replaced static initialization of values from the directory configuration with
** a method called at server bootstrap.
** DDF 20230707 Initialize propathFixed with a default empty string.
** 058 GBB 20230718 Implemented SESSION:FWD-LOGFILE getter method.
** 059 SAT 20231017 Updated getKeyValue() to return unknown when Environments.getKeyValue() return
** empty string.
** SAT 20231020 Replaced result.equals("") from getKeyValue() with result.isEmpty(). Updated
** getCurrentEnvironmentName() to return "" instead of null when no custom
** environment is found.
** 060 GBB 20231027 Fallback env for non-Win, non-ini LOAD, UNLOAD, SET, GET-KEY-VALUE, PUT-KEY-VALUE.
** 061 AD 20240125 Changed access modifier of method "addSourcePathListener(Consumer)" to public.
** 062 SB 20240617 Use DirectoryManager to get operating system. Refs #8368.
** 063 ES 20240521 Propagate a ConditionException.
** 064 GBB 20240709 Hard-coded config name replaced by ConfigItem constant.
** 065 GBB 20240826 Replacing Environments with OSResourceManager.getEnvironments().
** Removing OperatingSystemInspector. Call setCurrentLanguage rpc.
** 066 GBB 20240829 Adds back a check if running as server before using OSResourceManager.
** 067 ICP 20250131 Used character constants to leverage cached instances.
** 068 GBB 20250403 Don't make the client call in setCurrentLanguage for MSA.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.util;
import java.io.*;
import java.util.*;
import java.util.Map.Entry;
import java.util.function.*;
import com.goldencode.p2j.security.*;
import com.goldencode.p2j.ui.*;
import com.goldencode.p2j.directory.*;
import com.goldencode.p2j.net.*;
import com.goldencode.p2j.util.appserver.*;
import com.goldencode.p2j.util.logging.*;
import com.goldencode.p2j.util.osresource.*;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
/**
* Progress compatible environment-related static utility methods.
* <p>
* This supports some common global variables that are called built-in
* functions in Progress but which take no parameters, not even empty
* parenthesis. These variables are read-only (they cannot be assigned),
* except for <code>PROPATH</code> which can be modified as it can be in
* Progress as well.
* <p>
* The following is the list of supported variables:
* <p>
* <pre>
* Progress Variable Java Equivalent
* --------------------- ----------------------
* current-language {@link #getCurrentLanguage}
* dataservers {@link #getDataServerList}
* gateways {@link #getDataServerList}
* opsys {@link #getOperatingSystem}
* progress {@link #getRuntimeType}
* promsgs {@link #getMessageSource}
* propath {@link #getSearchPath}
* proversion {@link #getVersion}
* </pre>
* <p>
* <pre>
* Progress Language Statement Java Equivalent
* --------------------------- ----------------------
* get-key-value {@link #getKeyValue}
* load {@link #load}
* propath {@link #setSearchPath}
* put-key-value {@link #setKeyValue}
* unload {@link #unload}
* use {@link #use}
* </pre>
* <p>
* <pre>
* Progress Function Java Equivalent
* --------------------- ----------------------
* program-name {@link #getSourceName}
* </pre>
* <p>
* <pre>
* System Handle:Attribute Java Equivalent
* ------------------------- ----------------------
* session:batch-mode {@link #isBatchMode} (get)
* session:temp-directory {@link #getTempDirectory} (get)
* session:fwd-logfile {@link #getFwdLogfile} (get)
* </pre>
*/
public class EnvironmentOps
{
/** Progress OPSYS string for Unix systems. */
public static final String OS_UNIX = "UNIX";
/** Progress OPSYS string for Windows systems. */
public static final String OS_WIN = "WIN32";
/** Progress PROPATH separator string for all systems. */
public static final String PROPATH_SEPARATOR = ",";
/**
* Maps known Java os.name system property entries to a given Progress
* OPSYS string. Entries should be inserted in lowercase and all keys
* should be lowercased before reading from the map.
*/
public static final Map<String, String> osMap = new HashMap<>();
/**
* The helper splitter that trims strings and omits empty strings.
*/
private static final Splitter SPLITTER = Splitter.on(",").omitEmptyStrings().trimResults();
/** Logger */
private static final CentralLogger LOG = CentralLogger.get(EnvironmentOps.class);
/** The fixed default part of the PROPATH value. */
private static String propathFixed = "";
static
{
// unix variants
osMap.put("linux", OS_UNIX);
osMap.put("aix", OS_UNIX);
osMap.put("solaris", OS_UNIX);
osMap.put("sunos", OS_UNIX);
osMap.put("hp-ux", OS_UNIX);
osMap.put("freebsd", OS_UNIX);
osMap.put("mac os x", OS_UNIX);
// windows variants
osMap.put("windows xp", OS_WIN);
osMap.put("windows 2000", OS_WIN);
osMap.put("windows nt", OS_WIN);
osMap.put("windows me", OS_WIN);
osMap.put("windows 98", OS_WIN);
osMap.put("windows 95", OS_WIN);
}
/** Stores context-local state variables. */
private static ContextContainer work = new ContextContainer();
/**
* Method called at server bootstrap that initializes values from the directory
* configuration. Until this method is called, default values are used.
*/
public static void bootstrap()
{
// Initialize default PROPATH value
propathFixed = fixupPropath(Utils.getDirectoryNodeString(null, "searchpath-fixed", ""));
}
/**
* Retrieves the session-specific current language, the character value of CURRENT-LANGUAGE.
*
* @return The name of the current language.
*/
public static character getCurrentLanguage()
{
return character.of(work.obtain().currentLanguage);
}
/**
* Retrieves the string value of the session-specific current language.
*
* @return The name of the current language.
*/
public static String getCurrentLanguageString()
{
return work.obtain().currentLanguage;
}
/**
* Sets the name of the current language being used for session.
*
* @param val
* The new CURRENT-LANGUAGE value.
*/
public static void setCurrentLanguage(String val)
{
work.obtain().currentLanguage = val;
TranslationManager tm = TranslationManager.getInstance();
tm.setCurrentLanguages(val);
OSResourceManager.getEnvironments().setKeyValue("client", "lang", val);
if (!MultiSessionAppserverManager.getInstance().isMsaContext())
{
LogicalTerminal.getClient().setCurrentLanguage(val);
}
}
/**
* Sets the name of the current language being used for session.
*
* @param val
* The new CURRENT-LANGUAGE value.
*/
public static void setCurrentLanguage(character val)
{
setCurrentLanguage(val.isUnknown() ? null : val.toStringMessage());
}
/**
* Gets the list of the data servers available in Progress compatible
* environment.
* <p>
* The value returned may have been found via a search algorithm that
* is account (user or process) specific or group specific
* within the current server or a global default for all servers.
* <p>
* The implementation iteratively looks up the directory node under:
* /server/<serverID>/runtime/<account_or_group>/dataServers
* <p>
* If no user/process or group nodes are present, then this is checked:
* /server/<serverID>/runtime/default/dataServers
* <p>
* If no /server/<serverID>/runtime node exists, this is checked
* (it is the global default area for all servers):
* /server/default/runtime/<account_or_group>/dataServers
* <p>
* Finally, if no user/process or group nodes are present in the global
* default area, then this is checked:
* /server/default/runtime/default/dataServers
* <p>
* If no value is found via this lookup, then the default value of
* "PROGRESS,ORACLE,AS400,ODBC,MSS" will be returned.
*
* @return The list of data servers.
*/
public static character getDataServerList()
{
String def = "PROGRESS,ORACLE,AS400,ODBC,MSS";
String dserver = Utils.getDirectoryNodeString(null,
"dataServers",
def);
return new character(dserver);
}
/**
* Gets the name of the operating system upon which the Progress compatible
* client environment is running. The first attempt to resolve this value
* will occur via a lookup in the directory.
* <p>
* The value returned may have been found via a search algorithm that
* is account (user or process) specific or group specific
* within the current server or a global default for all servers.
* <p>
* The implementation iteratively looks up the directory node under:
* /server/<serverID>/runtime/<account_or_group>/opsys/override
* <p>
* If no user/process or group nodes are present, then this is checked:
* /server/<serverID>/runtime/default/opsys/override
* <p>
* If no /server/<serverID>/runtime node exists, this is checked
* (it is the global default area for all servers):
* /server/default/runtime/<account_or_group>/opsys/override
* <p>
* Finally, if no user/process or group nodes are present in the global
* default area, then this is checked:
* /server/default/runtime/default/opsys/override
* <p>
* If no value is found via this lookup, then the Java system property
* "os.name" is queried. This is then translated via an optional
* mapping table in the directory (that same paths and precedence as
* above but ending in "opsys/mapping/<lowercased_system_propert_name>.
* <p>
* If no mapping is found, then an internal mapping table in this class
* is consulted. This table maps the following:
* <p>
* <pre>
* os.name Progress OPSYS
* ---------------- ---------------
* "linux" "UNIX"
* "aix" "UNIX"
* "solaris" "UNIX"
* "sunos" "UNIX"
* "hp-ux" "UNIX"
* "freebsd" "UNIX"
* "mac os x" "UNIX"
* "windows xp" "WIN32"
* "windows 2000" "WIN32"
* "windows nt" "WIN32"
* "windows me" "WIN32"
* "windows 98" "WIN32"
* "windows 95" "WIN32"
* </pre>
* <p>
* If there is still no match, then any <code>os.name</code> with the
* lowercase text "unix" will return "UNIX" and any <code>os.name</code>
* with the lowercase text "windows" will return "WIN32".
* <p>
* If there is still no match, the <code>os.name</code> returned by
* Java will be returned.
*
* @return The name of the current operating system.
*/
public static character getOperatingSystem()
{
WorkArea wa = work.obtain();
if (wa.clientOsName == null)
{
// look for a hard coded value
String osname = DirectoryManager.getInstance().getString(Directory.ID_RELATIVE, "opsys/override", "");
// if there is a hard coded operating system override, use it
if (osname.length() == 0)
{
osname = getClientOpSysName(wa);
}
// Store per session OS name string to avoid every time call down to client
wa.clientOsName = osname;
// Calculate the cache for boolean Windows flag
wa.isWindowsClient = osname.toLowerCase().contains("win");
}
return character.of(wa.clientOsName);
}
/**
* Gets the name of the operating system being upon which the Progress compatible client
* environment is running. This method differs from {@link #getOperatingSystem()} in that
* the first attempt to resolve this value will NOT occur via a lookup in the directory under
* the {@code opsys/override} key. However, the rest of the resolution of this value is the
* same as is described in {@link #getOperatingSystem()}.
*
* @return The name of the current operating system upon which the client is running.
*/
public static character getRuntimeOperatingSystem()
{
WorkArea wa = work.obtain();
if (wa.clientRuntimeOsName == null)
{
wa.clientRuntimeOsName = getClientOpSysName(wa);
}
return character.of(wa.clientRuntimeOsName);
}
/**
* Check if client legacy platform is Windows OS.
* <p>
* Sometimes we need to specify a legacy platform regarding the real platform on which the
* clients actually runs. @link ColorSpec#convert. By default the client legacy platform is
* the same as OPSYS built-in returned value in Progress.
* <p>
* The value is read from server directory. If no value is set for <code>legacy-system/opsys</code>
* then the returned value is the same as returned by the <code>getOperatingSystem</code> call.
* If set in the directory, the value of opsys MUST be UNIX or WIN32.
*
* @return <code>true</code> if the legacy platform is Windows OS.
*/
public static boolean isLegacyPlatformWindows()
{
WorkArea wa = work.obtain();
if (wa.legacyPlatform == null)
{
getLegacyPlatform();
}
return wa.legacyPlatformWindows;
}
/**
* Checks is the current OS the P2J is running on belongs to the Windows
* family.
*
* @return <code>true</code> if the current OS is from Windows family or
* <code>false</code> otherwise.
*/
public static boolean isUnderWindowsFamily()
{
WorkArea wa = work.obtain();
if (wa.clientOsName == null)
{
getOperatingSystem();
}
return wa.isWindowsClient;
}
/**
* Gets the current runtime type being provided by the Progress
* compatible environment. The P2J environment only provides a
* "Full" environment.
*
* @return Returns the directory value "legacy-system/progress" if it exists
* or defaults to "Run-time"
*/
public static character getRuntimeType()
{
return new character(Utils.getDirectoryNodeString(DirectoryService.getInstance(),
"legacy-system/progress",
"Run-time",
true));
}
/**
* Gets the name of the directory where temporary files are stored.
* Corresponds with with the <code>SESSION:TEMP-DIRECTORY</code> system
* handle attribute.
*
* @return Name of temporary directory.
*/
public static character getTempDirectory()
{
FileSystem fileSystem = OSResourceManager.getFileSystem();
String tempDir = fileSystem.getTempDirectory();
String fileSeparator = fileSystem.getFileSeparator();
if (!tempDir.endsWith(fileSeparator))
tempDir += fileSeparator;
return new character(tempDir);
}
/**
* Gets the name of the current message source being used for the Progress
* compatible environment.
*
* @return Always returns the string "promsgs".
*/
public static character getMessageSource()
{
return new character("promsgs");
}
/**
* Sets the current message source being used.
*
* @param src
* The message source.
*/
public static void setMessageSource(String src)
{
UnimplementedFeature.unsupported("PROMSGS builtin variable can not be changed.");
}
/**
* Sets the current message source being used.
*
* @param src
* The message source.
*/
public static void setMessageSource(Text src)
{
UnimplementedFeature.unsupported("PROMSGS builtin variable can not be changed.");
}
/**
* Sets the Progress search path on the TARGET P2J system which is
* used for the Progress compatible environment. This corresponds to the
* assignment of the "PROPATH" variable in Progress and represents a
* mechanism to override the default that is available from the directory.
* <p>
* The PROPATH is also used in the implementation of
* {@link FileSystemOps#searchPath}. The actual I/O operations when
* executed via the Progress compatible interface (see
* {@link FileSystemOps} and {@link Stream}) for a P2J system are executed
* on the P2J client. This is the TARGET system on which this path is
* valid.
* <p>
* This must be distinguished from the original Progress SOURCE system's
* PROPATH which must be maintained for certain runtime translations
* of dynamically generated names (the algorithms of which were designed
* with that path assumed) but which is not necessarily a valid path on
* the system on which this is running. The original PROPATH can be
* obtained via the {@link #getLegacySearchPath} method.
*
* @param path
* The replacement path to Progress source files.
* @param cvt
* This marks if the function is being called at conversion time or not.
*/
public static void setSearchPath(String path, boolean cvt)
{
if (path != null)
{
WorkArea wa = work.obtain();
// the passed in value is the variable PROPATH part; since this was potentially
// built up at runtime using converted 4GL, the code might be relying upon
// the legacy system-specific propath separator character instead of the comma
// (which is platform neutral); convert the legacy path-separator to the comma
// here
String override = fixupPropath(path);
String newpath = override;
// combine the final value from variable and fixed parts, if needed
if (!propathFixed.equals(""))
{
newpath = newpath + PROPATH_SEPARATOR + propathFixed;
}
// save the new effective propath
wa.propath = newpath;
// save the variable part
wa.propathOverride = override;
if (!cvt)
{
wa.notifySourcePath(wa.propath);
}
}
}
/**
* Sets the Progress search path on the TARGET P2J system which is
* used for the Progress compatible environment. This corresponds to the
* assignment of the "PROPATH" variable in Progress and represents a
* mechanism to override the default that is available from the directory.
* <p>
* The PROPATH is also used in the implementation of
* {@link FileSystemOps#searchPath}. The actual I/O operations when
* executed via the Progress compatible interface (see
* {@link FileSystemOps} and {@link Stream}) for a P2J system are executed
* on the P2J client. This is the TARGET system on which this path is
* valid.
* <p>
* This must be distinguished from the original Progress SOURCE system's
* PROPATH which must be maintained for certain runtime translations
* of dynamically generated names (the algorithms of which were designed
* with that path assumed) but which is not necessarily a valid path on
* the system on which this is running. The original PROPATH can be
* obtained via the {@link #getLegacySearchPath} method.
*
* @param path
* The replacement path to Progress source files.
*/
public static void setSearchPath(String path)
{
setSearchPath(path, false);
}
/**
* Sets the Progress search path on the TARGET P2J system which is
* used for the Progress compatible environment. This corresponds to the
* assignment of the "PROPATH" variable in Progress and represents a
* mechanism to override the default that is available from the directory.
* <p>
* The PROPATH is also used in the implementation of
* {@link FileSystemOps#searchPath}. The actual I/O operations when
* executed via the Progress compatible interface (see
* {@link FileSystemOps} and {@link Stream}) for a P2J system are executed
* on the P2J client. This is the TARGET system on which this path is
* valid.
* <p>
* This must be distinguished from the original Progress SOURCE system's
* PROPATH which must be maintained for certain runtime translations
* of dynamically generated names (the algorithms of which were designed
* with that path assumed) but which is not necessarily a valid path on
* the system on which this is running. The original PROPATH can be
* obtained via the {@link #getLegacySearchPath} method.
*
* @param path
* The replacement path to Progress source files.
*/
public static void setSearchPath(character path)
{
setSearchPath(path.toStringMessage());
}
/**
* Gets any context-specific override for the Progress search path which
* was previously set via {@link #setSearchPath}.
*
* @return The path override or <code>null</code> if no override was
* ever set.
*/
public static String getSearchPathOverride()
{
return work.obtain().propath;
}
/**
* Gets the Progress search path on the TARGET P2J system which is
* used for the Progress compatible environment. This corresponds to the
* use of the "PROPATH" variable in Progress and is also used in the
* implementation of {@link FileSystemOps#searchPath}. The actual I/O
* operations when executed via the Progress compatible interface (see
* {@link FileSystemOps} and {@link Stream}) for a P2J system are executed
* on the P2J client. This is the TARGET system on which this path is
* valid.
* <p>
* This must be distinguished from the original Progress SOURCE system's
* PROPATH which must be maintained for certain runtime translations
* of dynamically generated names (the algorithms of which were designed
* with that path assumed) but which is not necessarily a valid path on
* the system on which this is running. The original PROPATH can be
* obtained via the {@link #getLegacySearchPath} method.
* <p>
* If a specific PROPATH has been assigned in this user's context, then
* that "override" will be returned. Otherwise, the directory will be
* consulted for the stored version.
* <p>
* The value returned from the directory may have been found via a search
* algorithm that is account (user or process) specific or group specific
* within the current server or a global default for all servers.
* <p>
* The implementation iteratively looks up the directory node under:
* /server/<serverID>/runtime/<account_or_group>/searchpath
* <p>
* If no user/process or group nodes are present, then this is checked:
* /server/<serverID>/runtime/default/searchpath
* <p>
* If no /server/<serverID>/runtime node exists, this is checked
* (it is the global default area for all servers):
* /server/default/runtime/<account_or_group>/searchpath
* <p>
* Finally, if no user/process or group nodes are present in the global
* default area, then this is checked:
* /server/default/runtime/default/searchpath
* <p>
* If no value is found via this lookup, then the default value of
* ".," will be returned.
*
* @return The path to Progress source files.
*/
public static character getSearchPath()
{
WorkArea wa = work.obtain();
String propath = wa.propath;
// is there a context-specific override?
if (propath == null)
{
// support the override
propath = wa.propathOverride;
// There is system default, add to the end
if (!propathFixed.equals(""))
{
propath = propath + PROPATH_SEPARATOR + propathFixed;
}
// Save the local copy of the effective propath
wa.propath = propath;
wa.notifySourcePath(propath);
}
return new character(propath);
}
/**
* Substitutes the path separator values with the comma character in
* the given string and returns the 4GL compatible PROPATH value that is
* the combination of the directory entries separated by comma.
*
* @param propathInitial
* The PROPATH that possibly needs to be fixed due to containing
* of the path separators instead of the expected comma values.
*
* @return The PROPATH value with comma separation instead of possible
* path separators.
*/
public static String fixupPropath(String propathInitial)
{
// First select the right path separator
String pathSep = Utils.getDirectoryNodeString(null, "path-separator", File.pathSeparator);
StringBuilder sb = new StringBuilder();
int pathSepLength = pathSep.length();
// The starting position of the next PROPATH element
int nextSegment = 0;
// Scan input parameter for the next occurence of the path separator.
// The next PROPATH element starting point is the sum of the current
// PROPATH element end point and length of the path sepatator.
for (int i = 0;
(i = propathInitial.indexOf(pathSep, nextSegment)) >= 0;
nextSegment = i + pathSepLength)
{
// Extract the PROPATH entry up to the nearest path separator
sb.append(propathInitial.substring(nextSegment, i));
// Replace path separator with comma
sb.append(PROPATH_SEPARATOR);
}
// We have to handle the case when new PROPATH sting is not finished
// with path separator.
if (nextSegment < propathInitial.length())
sb.append(propathInitial.substring(nextSegment));
return sb.toString();
}
/**
* Gets the original Progress SOURCE system's PROPATH which must be
* maintained for certain runtime translations of dynamically generated
* names (the algorithms of which were designed with that path assumed)
* but which is not necessarily a valid path on the system on which this
* is running.
* <p>
* The value returned may have been found via a search algorithm that
* is account (user or process) specific or group specific
* within the current server or a global default for all servers.
* <p>
* The implementation iteratively looks up the directory node under:
* /server/<serverID>/runtime/<account_or_group>/legacy-system/propath
* <p>
* If no user/process or group nodes are present, then this is checked:
* /server/<serverID>/runtime/default/legacy-system/propath
* <p>
* If no /server/<serverID>/runtime node exists, this is checked
* (it is the global default area for all servers):
* /server/default/runtime/<account_or_group>/legacy-system/propath
* <p>
* Finally, if no user/process or group nodes are present in the global
* default area, then this is checked:
* /server/default/runtime/default/legacy-system/propath
* <p>
* If no value is found via this lookup, then the default value of
* "" (empty string) will be returned.
* <p>
* This must be distinguished from Progress source file/search path on the
* TARGET P2J system which is used for the Progress compatible
* environment. To obtain that value, use {@link #getSearchPath}.
*
* @return The path to Progress source files.
*/
public static String getLegacySearchPath()
{
return Utils.getDirectoryNodeString(null, "legacy-system/propath", "");
}
/**
* Gets the path separator character used to parse the Progress search
* path (see {@link #getSearchPath}).
* <p>
* The value returned may have been found via a search algorithm that
* is account (user or process) specific or group specific
* within the current server or a global default for all servers.
* <p>
* The implementation iteratively looks up the directory node under:
* /server/<serverID>/runtime/<account_or_group>/path-separator
* <p>
* If no user/process or group nodes are present, then this is checked:
* /server/<serverID>/runtime/default/path-separator
* <p>
* If no /server/<serverID>/runtime node exists, this is checked
* (it is the global default area for all servers):
* /server/default/runtime/<account_or_group>/path-separator
* <p>
* Finally, if no user/process or group nodes are present in the global
* default area, then this is checked:
* /server/default/runtime/default/path-separator
* <p>
* If no value is found via this lookup, then the default value of
* ':' will be returned.
*
* @return The path to Progress source files.
*/
public static String getPathSeparator()
{
if (work == null)
{
return Utils.getDirectoryNodeString(null, "path-separator", File.pathSeparator);
}
WorkArea wa = work.obtain();
if (wa.clientPathSeparator == null)
{
// check the directory for an override value
String pathSep = Utils.getDirectoryNodeString(null, "path-separator", "");
// read OS value as fallback for path separator value
if (pathSep.length() == 0 && isServer())
{
pathSep = OSResourceManager.getFileSystem().getFileSeparator();
}
// if we still don't have a value, just use the server's OS value
if (pathSep == null || pathSep.length() == 0)
{
pathSep = File.pathSeparator;
}
// save the calculated value
wa.clientPathSeparator = pathSep;
}
return wa.clientPathSeparator;
}
/**
* Gets the original Progress SOURCE system's path separator which must be
* maintained for certain runtime translations of dynamically generated
* names (the algorithms of which were designed with that path assumed)
* but which is not necessarily a valid path on the system on which this
* is running.
* <p>
* The value returned may have been found via a search algorithm that
* is account (user or process) specific or group specific
* within the current server or a global default for all servers.
* <p>
* The implementation iteratively looks up the directory node under:
* /server/<serverID>/runtime/<account_or_group>/legacy-system/path-separator
* <p>
* If no user/process or group nodes are present, then this is checked:
* /server/<serverID>/runtime/default/legacy-system/path-separator
* <p>
* If no /server/<serverID>/runtime node exists, this is checked
* (it is the global default area for all servers):
* /server/default/runtime/<account_or_group>/legacy-system/path-separator
* <p>
* Finally, if no user/process or group nodes are present in the global
* default area, then this is checked:
* /server/default/runtime/default/legacy-system/path-separator
* <p>
* If no value is found via this lookup, then the default value of
* ":" will be returned.
* <p>
* This must be distinguished from the path separator on the (runtime)
* TARGET P2J system which is used for the Progress compatible
* environment. To obtain that value, use {@link #getPathSeparator}.
*
* @return The path separator used on the SOURCE system.
*/
public static String getLegacyPathSeparator()
{
return Utils.getDirectoryNodeString(null,
"legacy-system/path-separator",
File.pathSeparator);
}
/**
* Gets the list of directories in the Progress source file path. Each
* element of the array is a directory in the source path. This source
* path is split based on the path separator used in this path
* specification which may be different than that reported by the
* system (<code>File.pathSeparator</code>. The resulting array is
* returned to the caller.
* <p>
* The {@link #getSearchPath} is used for the path specification and
* the {@link #getPathSeparator} is used to split the path.
*
* @return The array of directory names.
*/
public static String[] getSearchPathList()
{
return splitPath(getSearchPath().toStringMessage(), PROPATH_SEPARATOR);
}
/**
* Gets the original Progress SOURCE system's file separator which must be
* maintained for certain runtime translations of dynamically generated
* names (the algorithms of which were designed with that path assumed)
* but which is not necessarily a valid path on the system on which this
* is running.
* <p>
* The value returned may have been found via a search algorithm that
* is account (user or process) specific or group specific
* within the current server or a global default for all servers.
* <p>
* The implementation iteratively looks up the directory node under:
* /server/<serverID>/runtime/<account_or_group>/legacy-system/file-separator
* <p>
* If no user/process or group nodes are present, then this is checked:
* /server/<serverID>/runtime/default/legacy-system/file-separator
* <p>
* If no /server/<serverID>/runtime node exists, this is checked
* (it is the global default area for all servers):
* /server/default/runtime/<account_or_group>/legacy-system/file-separator
* <p>
* Finally, if no user/process or group nodes are present in the global
* default area, then this is checked:
* /server/default/runtime/default/legacy-system/file-separator
* <p>
* If no value is found via this lookup, then the default value of
* "/" will be returned.
*
* @return The legacy system's file separator.
*/
public static String getLegacyFileSeparator()
{
return work.obtain().legacyFileSeparator;
}
/**
* Gets the absolute path prefix map from the current directory settings.
*
* @return The absolute path prefix map
*/
public static Map<String, String> getLegacyAbsolutePathPrefixMap()
{
WorkArea wa = work.obtain();
if (wa.pathPrefixMap == null)
{
wa.pathPrefixMap = Utils.getDirectoryNodeMap(DirectoryService.getInstance(),
"legacy-system/absolute-path-prefix-map",
null,
true,
false);
}
return wa.pathPrefixMap;
}
/**
* Gets the environment variables map from the current directory settings.
*
* @param caseInsensitiveKeys
* {@code true} to use case-insensitive keys for the environment variables map.
*
* @return The environment variables map
*/
public static Map<String, String> getLegacyEnvironmentVariables(boolean caseInsensitiveKeys)
{
return Utils.getDirectoryNodeMap(DirectoryService.getInstance(),
"legacy-system/environment-variables",
null,
true,
caseInsensitiveKeys);
}
/**
* Gets the original Progress SOURCE file system's case-sensitive setting
* which must be maintained for certain runtime translations of dynamically
* generated names (the algorithms of which were designed with that path
* assumed) but which is not necessarily a valid path on the system on
* which this is running.
* <p>
* The value returned may have been found via a search algorithm that
* is account (user or process) specific or group specific
* within the current server or a global default for all servers.
* <p>
* The implementation iteratively looks up the directory node under:
* /server/<serverID>/runtime/<account_or_group>/legacy-system/case-sensitive
* <p>
* If no user/process or group nodes are present, then this is checked:
* /server/<serverID>/runtime/default/legacy-system/case-sensitive
* <p>
* If no /server/<serverID>/runtime node exists, this is checked
* (it is the global default area for all servers):
* /server/default/runtime/<account_or_group>/legacy-system/case-sensitive
* <p>
* Finally, if no user/process or group nodes are present in the global
* default area, then this is checked:
* /server/default/runtime/default/legacy-system/case-sensitive
* <p>
* If no value is found via this lookup, then the default value of
* <code>true</code> will be returned.
*
* @return <code>true</code> if the legacy file system was
* case-sensitive.
*/
public static boolean getLegacyCaseSensitive()
{
WorkArea wa = work.obtain();
if (wa.legacyCaseSensitive == null)
{
wa.legacyCaseSensitive = Utils.getDirectoryNodeBoolean(null,
"legacy-system/case-sensitive",
!isUnderWindowsFamily());
}
return wa.legacyCaseSensitive;
}
/**
* Gets the current version string of the Progress compatible environment.
* <p>
* The value returned may have been found via a search algorithm that
* is account (user or process) specific or group specific
* within the current server or a global default for all servers.
* <p>
* The implementation iteratively looks up the directory node under:
* /server/<serverID>/runtime/<account_or_group>/version
* <p>
* If no user/process or group nodes are present, then this is checked:
* /server/<serverID>/runtime/default/version
* <p>
* If no /server/<serverID>/runtime node exists, this is checked
* (it is the global default area for all servers):
* /server/default/runtime/<account_or_group>/version
* <p>
* Finally, if no user/process or group nodes are present in the global
* default area, then this is checked:
* /server/default/runtime/default/version
* <p>
* If no value is found via this lookup, then the default value of
* "11.6" will be returned.
*
* @return The current version string.
*/
public static character getVersion()
{
return new character(versionWorker(0));
}
/**
* Gets the current version string of the Progress compatible environment.
* <p>
* The value returned may have been found via a search algorithm that
* is account (user or process) specific or group specific
* within the current server or a global default for all servers.
* <p>
* The implementation iteratively looks up the directory node under:
* /server/<serverID>/runtime/<account_or_group>/version
* <p>
* If no user/process or group nodes are present, then this is checked:
* /server/<serverID>/runtime/default/version
* <p>
* If no /server/<serverID>/runtime node exists, this is checked
* (it is the global default area for all servers):
* /server/default/runtime/<account_or_group>/version
* <p>
* Finally, if no user/process or group nodes are present in the global
* default area, then this is checked:
* /server/default/runtime/default/version
* <p>
* If no value is found via this lookup, then the default value of
* "11.6.3.0.1407" will be returned.
*
* @param lvl
* The level of verbosity. 0 for the short version (11.6) or
* 1 for the long version (11.6.3.0.1407). Any other value
* generates an error except for unknown value which is treated
* as 0.
*
* @return The current version string.
*/
public static character getVersion(integer lvl)
{
int i = 0;
if (lvl != null && !lvl.isUnknown())
{
i = lvl.intValue();
}
return new character(getVersion(i));
}
/**
* Gets the current version string of the Progress compatible environment.
* <p>
* The value returned may have been found via a search algorithm that
* is account (user or process) specific or group specific
* within the current server or a global default for all servers.
* <p>
* The implementation iteratively looks up the directory node under:
* /server/<serverID>/runtime/<account_or_group>/version
* <p>
* If no user/process or group nodes are present, then this is checked:
* /server/<serverID>/runtime/default/version
* <p>
* If no /server/<serverID>/runtime node exists, this is checked
* (it is the global default area for all servers):
* /server/default/runtime/<account_or_group>/version
* <p>
* Finally, if no user/process or group nodes are present in the global
* default area, then this is checked:
* /server/default/runtime/default/version
* <p>
* If no value is found via this lookup, then the default value of
* "11.6.3.0.1407" will be returned.
*
* @param lvl
* The level of verbosity. 0 for the short version (11.6) or
* 1 for the long version (11.6.3.0.1407). Any other value
* generates an error except for unknown value which is treated
* as 0.
*
* @return The current version string.
*/
public static character getVersion(long lvl)
{
if (lvl != 0 && lvl != 1)
{
String msg = "The parameter value for the PROVERSION function, if specified, " +
"can only be 0 or 1";
ErrorManager.recordOrThrowError(16742, msg);
return new character();
}
return new character(versionWorker(lvl));
}
/**
* Retrieve the Progress version number. If it has not been cached yet, it will read it.
*
* @return The current Progress version number.
*/
public static float versionNumber()
{
WorkArea wa = work.obtain();
// If we have not cached the value, get it
if (wa.versionNumber == 0.0f)
{
versionWorker(0);
}
return wa.versionNumber;
}
/**
* Check if the Progress major version is 9.
*
* @return <code>true</code> if Progress major version is 9.
*/
public static boolean isV9()
{
float versionNumber = versionNumber();
return (versionNumber >= 9.0f && versionNumber < 10.0f);
}
/**
* Check if the Progress major version is 10.
*
* @return <code>true</code> if Progress major version is 10.
*/
public static boolean isV10()
{
float versionNumber = versionNumber();
return (versionNumber >= 10.0f && versionNumber < 11.0f);
}
/**
* Check if the Progress major version is 11.
*
* @return <code>true</code> if Progress major version is 11.
*/
public static boolean isV11()
{
float versionNumber = versionNumber();
return (versionNumber >= 11.0f && versionNumber < 12.0f);
}
/**
* Check if the Progress major version is 12.
*
* @return <code>true</code> if Progress major version is 12.
*/
public static boolean isV12()
{
float versionNumber = versionNumber();
return (versionNumber >= 12.0f && versionNumber < 13.0f);
}
/**
* Returns the Progress source file name that matches the program at the given index in the
* stack trace if the given index position exists. The Progress source file name will be
* returned if possible using the {@link ProcedureManager#getStackEntry}.
*
* @param index
* The 1-based index position in the call stack to be returned. Decimal values are
* not allowed in the 4GL (compile-time error), but any 64-bit integer value is
* allowed.
*
* @return The original Progress 4GL source file name (if the class is a converted 4GL
* program). If the index is out of bounds or is unknown, then the unknown value
* is returned.
*/
public static character getSourceName(int64 index)
{
if (index.isUnknown())
{
return character.UNKNOWN;
}
return getSourceName(index.longValue());
}
/**
* Returns the Progress source file name that matches the program at the given index in the
* stack trace if the given index position exists. The Progress source file name will be
* returned if possible using the {@link ProcedureManager#getStackEntry}.
*
* @param index
* The 1-based index position in the call stack to be returned. Decimal values are
* not allowed in the 4GL (compile-time error), but any 64-bit integer value is
* allowed.
*
* @return The original Progress 4GL source file name (if the class is a converted 4GL
* program). If the index is out of bounds, then the unknown value is returned.
*/
public static character getSourceName(long index)
{
return new character(ProcedureManager.getStackEntry((int) (index - 1)));
}
/**
* Gets the supported helper applications with their mime types.
*
* @return The the supported helper applications
*/
public static Map<String, List<String>> getSupportedHelperApplications()
{
WorkArea wa = work.obtain();
if (wa.supportedHelperApplications == null)
{
Map<String, String> applicationsMap = ConfigItem.HELPER_APPS.read(null, DirectoryService.getInstance());
Map<String, List<String>> accum = new HashMap<>(applicationsMap.size());
for(Entry<String, String> entry : applicationsMap.entrySet())
{
List<String> listOfValues = Lists.<String>newArrayList(SPLITTER.split(
entry.getValue()));
accum.put(entry.getKey(), listOfValues);
};
wa.supportedHelperApplications = accum;
}
return wa.supportedHelperApplications;
}
/**
* Gets the current version string of the Progress compatible environment.
* <p>
* The value returned may have been found via a search algorithm that
* is account (user or process) specific or group specific
* within the current server or a global default for all servers.
* <p>
* The implementation iteratively looks up the directory node under:
* /server/<serverID>/runtime/<account_or_group>/version
* <p>
* If no user/process or group nodes are present, then this is checked:
* /server/<serverID>/runtime/default/version
* <p>
* If no /server/<serverID>/runtime node exists, this is checked
* (it is the global default area for all servers):
* /server/default/runtime/<account_or_group>/version
* <p>
* Finally, if no user/process or group nodes are present in the global
* default area, then this is checked:
* /server/default/runtime/default/version
* <p>
* If no value is found via this lookup, then the default value of
* "11.6.3.0.1407" will be returned.
* <p>
* Once found via the above steps, short and verbose version values are cached.
*
* @param lvl
* The level of verbosity. 0 for the short version (10.2B) or
* 1 for the long version (11.6.3.0.1407).
*
* @return The current version string.
*/
private static String versionWorker(long lvl)
{
WorkArea wa = work.obtain();
float versionNumber;
if (wa.versionShort == null || wa.versionVerbose == null)
{
String version = Utils.getDirectoryNodeString(null, "version", "11.6.3.0.1407");
int idx = version.indexOf('.');
if (idx != -1)
{
idx = version.indexOf('.', idx + 1);
}
wa.versionShort = (idx != -1) ? version.substring(0, idx) : version;
wa.versionVerbose = version;
String ver = version;
idx = ver.indexOf('.');
if (idx != -1)
{
idx = ver.indexOf('.', idx + 1);
}
version = (idx != -1) ? ver.substring(0, idx) : ver;
// At this point, there could be a non-numeric in version, like "10.2B"
// We'll pre-check but also catch NumberFormatExceptions in case
for (int i = 0; i < version.length(); i++)
{
if (!Character.isDigit(version.charAt(i)) && (version.charAt(i) != '.'))
{
version = version.substring(0, i);
break;
}
}
try
{
wa.versionNumber = Float.parseFloat(version);
}
catch (NumberFormatException e)
{
// Set a default value
wa.versionNumber = 11.6f;
}
}
return (lvl == 0) ? wa.versionShort : wa.versionVerbose;
}
/**
* Splits a path using the alternative separator string
*
* @param path
* The <code>String</code> path to split into an array of directory
* names.
* @param separator
* The <code>String</code> separator to split into an array of directory
* names.
*
* @return The array of directory names.
*/
private static String[] splitPath(String path, String separator)
{
if (separator == null)
{
separator = getPathSeparator();
}
if (path != null)
{
return path.split(separator, -1);
}
else
{
return null;
}
}
/**
* Reports if the current context is running in batch mode.
* <p>
* The value returned may have been found via a search algorithm that
* is account (user or process) specific or group specific
* within the current server or a global default for all servers.
* <p>
* The implementation iteratively looks up the directory node under:
* /server/<serverID>/runtime/<account_or_group>/batchMode
* <p>
* If no user/process or group nodes are present, then this is checked:
* /server/<serverID>/runtime/default/batchMode
* <p>
* If no /server/<serverID>/runtime node exists, this is checked
* (it is the global default area for all servers):
* /server/default/runtime/<account_or_group>/batchMode
* <p>
* Finally, if no user/process or group nodes are present in the global
* default area, then this is checked:
* /server/default/runtime/default/batchMode
* <p>
* If no value is found via this lookup, then the default value of
* <code>false</code> will be returned.
*
* @return <code>true</code> if the current context is in batch mode.
*/
public static logical isBatchMode()
{
WorkArea wa = work.obtain();
if (wa.batchMode == null)
{
wa.batchMode = Utils.getDirectoryNodeBoolean(null, "batchMode", false);
}
return new logical(wa.batchMode);
}
/**
* Force the batch mode of the current context to the specified state.
*
* @param batchMode
* <code>true</code> if this context must be in batch mode.
*/
public static void setBatchMode(boolean batchMode)
{
WorkArea wa = work.obtain();
wa.batchMode = batchMode;
}
/**
* Sets the FWD client logger file path of this session.
*
* @param fwdLogfile
* The FWD client logger file path of this session.
*/
public static void setFwdLogfile(String fwdLogfile)
{
WorkArea wa = work.obtain();
wa.fwdLogfile = fwdLogfile;
}
/**
* Returns the FWD client logger file path of this session. The path has specifiers and placeholders
* resolved.
*
* @return See above.
*/
public static character getFwdLogfile()
{
return new character(work.obtain().fwdLogfile);
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
*/
public static void load(character env)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
load(env, (character)null, new logical(false), (character)null);
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
*/
public static void load(String env)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
load(new character(env), (character)null, new logical(false), (character)null);
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param flagNew
* If <code>true</code> - the new file or registry key will be
* created overwriting the possibly existed one.
*/
public static void load(character env, boolean flagNew)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
load(env, (character)null, new logical(flagNew), (character)null);
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param flagNew
* If <code>true</code> - the new file or registry key will be
* created overwriting the possibly existed one.
*/
public static void load(String env, boolean flagNew)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
load(new character(env), (character)null, new logical(flagNew), (character)null);
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param flagNew
* If <code>true</code> - the new file or registry key will be
* created overwriting the possibly existed one.
* @param baseKey
* When the parameter is specified the serching is performed under
* the particular key value. It is possible to specify "INI" as
* parameter. In this case search or creation will be dome inside
* initialization file specified in env parameter.
*/
public static void load(character env, boolean flagNew, character baseKey)
{
load(env, (character)null, new logical(flagNew), baseKey);
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param flagNew
* If <code>true</code> - the new file or registry key will be
* created overwriting the possibly existed one.
* @param baseKey
* When the parameter is specified the serching is performed under
* the particular key value. It is possible to specify "INI" as
* parameter. In this case search or creation will be dome inside
* initialization file specified in env parameter.
*/
public static void load(character env, boolean flagNew, String baseKey)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
load(env, (character)null, new logical(flagNew), new character(baseKey));
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param flagNew
* If <code>true</code> - the new file or registry key will be
* created overwriting the possibly existed one.
* @param baseKey
* When the parameter is specified the serching is performed under
* the particular key value. It is possible to specify "INI" as
* parameter. In this case search or creation will be dome inside
* initialization file specified in env parameter.
*/
public static void load(String env, boolean flagNew, String baseKey)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
load(new character(env), (character)null, new logical(flagNew), new character(baseKey));
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param flagNew
* If <code>true</code> - the new file or registry key will be
* created overwriting the possibly existed one.
* @param baseKey
* When the parameter is specified the serching is performed under
* the particular key value. It is possible to specify "INI" as
* parameter. In this case search or creation will be dome inside
* initialization file specified in env parameter.
*/
public static void load(String env, boolean flagNew, character baseKey)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
load(new character(env), (character)null, new logical(flagNew), baseKey);
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
*/
public static void load(character env, character directory)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
load(env, directory, new logical(false), null);
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
*/
public static void load(String env, character directory)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
load(new character(env), directory, new logical(false), null);
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
*/
public static void load(character env, String directory)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
load(env, new character(directory), new logical(false), null);
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
*/
public static void load(String env, String directory)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
load(new character(env), new character(directory), new logical(false), null);
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
* @param flagNew
* If <code>true</code> - the new file or registry key will be
* created overwriting the possibly existed one.
*/
public static void load(character env, character directory, boolean flagNew)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
load(env, directory, new logical(flagNew), (character)null);
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
* @param flagNew
* If <code>true</code> - the new file or registry key will be
* created overwriting the possibly existed one.
* @param baseKey
* When the parameter is specified the serching is performed under
* the particular key value. It is possible to specify "INI" as
* parameter. In this case search or creation will be dome inside
* initialization file specified in env parameter.
*/
public static void load(character env, character directory, boolean flagNew, character baseKey)
{
load(env, directory, new logical(flagNew), new character(baseKey));
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
* @param flagNew
* If <code>true</code> - the new file or registry key will be
* created overwriting the possibly existed one.
*/
public static void load(String env, character directory, boolean flagNew)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
load(new character(env), directory, new logical(flagNew), (character)null);
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
* @param flagNew
* If <code>true</code> - the new file or registry key will be
* created overwriting the possibly existed one.
*/
public static void load(character env, String directory, boolean flagNew)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
load(env, new character(directory), new logical(flagNew), (character)null);
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
* @param flagNew
* If <code>true</code> - the new file or registry key will be
* created overwriting the possibly existed one.
*/
public static void load(String env, String directory, boolean flagNew)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
load(new character(env), new character(directory), new logical(flagNew), (character)null);
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
* @param baseKey
* When the parameter is specified the serching is performed under
* the particular key value. It is possible to specify "INI" as
* parameter. In this case search or creation will be dome inside
* initialization file specified in env parameter.
*/
public static void load(character env, character directory, String baseKey)
{
load(env, directory, new logical(false), new character(baseKey));
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
* @param baseKey
* When the parameter is specified the serching is performed under
* the particular key value. It is possible to specify "INI" as
* parameter. In this case search or creation will be dome inside
* initialization file specified in env parameter.
*/
public static void load(character env, character directory, character baseKey)
{
load(env, directory, new logical(false), baseKey);
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
* @param baseKey
* When the parameter is specified the serching is performed under
* the particular key value. It is possible to specify "INI" as
* parameter. In this case search or creation will be dome inside
* initialization file specified in env parameter.
*/
public static void load(character env, String directory, String baseKey)
{
load(env, new character(directory), new logical(false), new character(baseKey));
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
* @param baseKey
* When the parameter is specified the serching is performed under
* the particular key value. It is possible to specify "INI" as
* parameter. In this case search or creation will be dome inside
* initialization file specified in env parameter.
*/
public static void load(String env, character directory, String baseKey)
{
load(new character(env), directory, new logical(false), new character(baseKey));
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
* @param baseKey
* When the parameter is specified the serching is performed under
* the particular key value. It is possible to specify "INI" as
* parameter. In this case search or creation will be dome inside
* initialization file specified in env parameter.
*/
public static void load(String env, character directory, character baseKey)
{
load(new character(env), directory, new logical(false), baseKey);
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
* @param baseKey
* When the parameter is specified the serching is performed under
* the particular key value. It is possible to specify "INI" as
* parameter. In this case search or creation will be dome inside
* initialization file specified in env parameter.
*/
public static void load(String env, String directory, String baseKey)
{
load(new character(env), new character(directory),
new logical(false), new character(baseKey));
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
* @param flagNew
* If <code>true</code> - the new file or registry key will be
* created overwriting the possibly existed one.
* @param baseKey
* When the parameter is specified the serching is performed under
* the particular key value. It is possible to specify "INI" as
* parameter. In this case search or creation will be dome inside
* initialization file specified in env parameter.
*/
public static void load(character env, character directory, boolean flagNew, String baseKey)
{
load(env, directory, new logical(flagNew), new character(baseKey));
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
* @param flagNew
* If <code>true</code> - the new file or registry key will be
* created overwriting the possibly existed one.
* @param baseKey
* When the parameter is specified the serching is performed under
* the particular key value. It is possible to specify "INI" as
* parameter. In this case search or creation will be dome inside
* initialization file specified in env parameter.
*/
public static void load(character env, String directory, boolean flagNew, String baseKey)
{
load(env, new character(directory), new logical(flagNew), new character(baseKey));
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
* @param flagNew
* If <code>true</code> - the new file or registry key will be
* created overwriting the possibly existed one.
* @param baseKey
* When the parameter is specified the serching is performed under
* the particular key value. It is possible to specify "INI" as
* parameter. In this case search or creation will be dome inside
* initialization file specified in env parameter.
*/
public static void load(String env, character directory, boolean flagNew, String baseKey)
{
load(new character(env), directory, new logical(flagNew), new character(baseKey));
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
* @param flagNew
* If <code>true</code> - the new file or registry key will be
* created overwriting the possibly existed one.
* @param baseKey
* When the parameter is specified the serching is performed under
* the particular key value. It is possible to specify "INI" as
* parameter. In this case search or creation will be dome inside
* initialization file specified in env parameter.
*/
public static void load(String env, character directory, boolean flagNew, character baseKey)
{
load(new character(env), directory, new logical(flagNew), baseKey);
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
* @param flagNew
* If <code>true</code> - the new file or registry key will be
* created overwriting the possibly existed one.
* @param baseKey
* When the parameter is specified the serching is performed under
* the particular key value. It is possible to specify "INI" as
* parameter. In this case search or creation will be dome inside
* initialization file specified in env parameter.
*/
public static void load(String env, String directory, boolean flagNew, String baseKey)
{
load(new character(env), new character(directory),
new logical(flagNew), new character(baseKey));
}
/**
* Creates application defaults implementing Progress LOAD statement.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
* @param flagNew
* If <code>true</code> - the new file or registry key will be
* created overwriting the possibly existed one.
* @param baseKey
* When the parameter is specified the serching is performed under
* the particular key value. It is possible to specify "INI" as
* parameter. In this case search or creation will be dome inside
* initialization file specified in env parameter.
*/
public static void load(character env, character directory, logical flagNew, character baseKey)
{
// Preparing initial parameters for EnvironmentAccessor interface
String envAsString = null;
String directoryAsString = null;
String baseKeyAsString = null;
boolean flagNewAsBoolean = false;
if (env != null)
{
envAsString = env.toStringMessage();
}
if (directory != null)
{
directoryAsString = directory.toStringMessage();
}
if (flagNew != null)
{
flagNewAsBoolean = flagNew.booleanValue();
}
if (baseKey != null)
{
baseKeyAsString = baseKey.toStringMessage();
}
if (!OSResourceManager.getEnvironments().load(envAsString, directoryAsString, flagNewAsBoolean, baseKeyAsString))
{
onLoadError(env, directory);
}
}
/**
* On load error message.
*
* @param env
* The environment to create or load. Can be the registry key,
* initialization file.
* @param directory
* The directory path for existing initialization file or place
* to create initialization file.
*/
private static void onLoadError(character env, character directory)
{
// This is support for NO-ERROR option
if (directory == null || directory.isUnknown())
{
ErrorManager.recordOrThrowError(4450, "The LOAD of " + env.toStringMessage() + " failed");
}
else
{
ErrorManager.recordOrThrowError(4449, "The LOAD of " + env.toStringMessage() +
" in path " + directory.toStringMessage() +
" failed");
}
}
/**
* Specifies environment defaults to use in subsequent windows.
*
* @param env
* The environment to load. If the parameter is empty string ("") the default
* environment becomes the current one.
*/
public static void use(String env)
{
use(new character(env));
}
/**
* Specifies environment defaults to use in subsequent windows.
*
* @param env
* The environment to load. If the parameter is empty string ("") the default
* environment becomes the current one.
*/
public static void use(character env)
{
String envNew = env != null ? env.toStringMessage() : "";
String envName = OSResourceManager.getEnvironments().use(envNew);
if (envName != null)
{
onUse(envName);
}
else
{
ErrorManager.conditionalShowErrorAndAbend(4452, "The USE of " + envNew + " failed");
}
}
/**
* Check if a custom environment is in use.
*
* @return See above.
*/
public static boolean isCustomEnvironment()
{
return (isUnderWindowsFamily() || isIniType())
&& OSResourceManager.getEnvironments().isCustomEnvironment();
}
/**
* Get the current environment's name.
*
* @return See above.
*/
public static String getEnvironmentName()
{
return OSResourceManager.getEnvironments().getEnvironmentName();
}
/**
* Get the current environment type.
*
* @return See above.
*/
public static String getEnvironmentType()
{
return OSResourceManager.getEnvironments().getEnvironmentType();
}
/**
* Get the name of the current printer.
*
* @return the printer name.
*/
public static character getCurrentPrinterName()
{
// getting current printer from system
String prnName = isServer() ?
OSResourceManager.getEnvironments().getCurrentPrinterName() :
null;
// return it if not null
return new character(prnName == null || prnName.isEmpty() ?
PrintingService.PDF_PRINTER_DESCR :
prnName);
}
/**
* Adds, modifies and deletes keys in current environment.
*
* @param section
* The name of the section containing the key to modify.
* @param value
* The new value of the key under modification.
*/
public static void setKeyValue(character section, String value)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
setKeyValue(section, (character)null, new character(value));
}
/**
* Adds, modifies and deletes keys in current environment.
*
* @param section
* The name of the section containing the key to modify.
* @param value
* The new value of the key under modification.
*/
public static void setKeyValue(String section, character value)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
setKeyValue(new character(section), (character)null, value);
}
/**
* Adds, modifies and deletes keys in current environment.
*
* @param section
* The name of the section containing the key to modify.
* @param value
* The new value of the key under modification.
*/
public static void setKeyValue(String section, String value)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
setKeyValue(new character(section), (character)null, new character(value));
}
/**
* Adds, modifies and deletes keys in current environment.
*
* @param section
* The name of the section containing the key to modify.
* @param value
* The new value of the key under modification.
*/
public static void setKeyValue(character section, character value)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
setKeyValue(section, (character)null, value);
}
/**
* Adds, modifies and deletes keys in current environment.
*
* @param section
* The name of the section containing the key to modify.
* @param key
* The name of the key key to modify or default key if not specified.
* @param value
* The new value of the key under modification.
*/
public static void setKeyValue(character section, character key, String value)
{
setKeyValue(section, key, new character(value));
}
/**
* Adds, modifies and deletes keys in current environment.
*
* @param section
* The name of the section containing the key to modify.
* @param key
* The name of the key key to modify or default key if not specified.
* @param value
* The new value of the key under modification.
*/
public static void setKeyValue(character section, String key, character value)
{
setKeyValue(section, new character(key), value);
}
/**
* Adds, modifies and deletes keys in current environment.
*
* @param section
* The name of the section containing the key to modify.
* @param key
* The name of the key key to modify or default key if not specified.
* @param value
* The new value of the key under modification.
*/
public static void setKeyValue(String section, character key, character value)
{
setKeyValue(new character(section), key, value);
}
/**
* Adds, modifies and deletes keys in current environment.
*
* @param section
* The name of the section containing the key to modify.
* @param key
* The name of the key key to modify or default key if not specified.
* @param value
* The new value of the key under modification.
*/
public static void setKeyValue(character section, String key, String value)
{
setKeyValue(section, new character(key), new character(value));
}
/**
* Adds, modifies and deletes keys in current environment.
*
* @param section
* The name of the section containing the key to modify.
* @param key
* The name of the key key to modify or default key if not specified.
* @param value
* The new value of the key under modification.
*/
public static void setKeyValue(String section, String key, character value)
{
setKeyValue(new character(section), new character(key), value);
}
/**
* Adds, modifies and deletes keys in current environment.
*
* @param section
* The name of the section containing the key to modify.
* @param key
* The name of the key key to modify or default key if not specified.
* @param value
* The new value of the key under modification.
*/
public static void setKeyValue(String section, character key, String value)
{
setKeyValue(new character(section), key, new character(value));
}
/**
* Adds, modifies and deletes keys in current environment.
*
* @param section
* The name of the section containing the key to modify.
* @param key
* The name of the key key to modify or default key if not specified.
* @param value
* The new value of the key under modification.
*/
public static void setKeyValue(String section, String key, String value)
{
setKeyValue(new character(section), new character(key), new character(value));
}
/**
* Adds, modifies and deletes keys in current environment.
*
* @param section
* The name of the section containing the key to modify.
* @param key
* The name of the key key to modify or default key if not specified.
* @param value
* The new value of the key under modification.
*/
public static void setKeyValue(character section, character key, character value)
{
String sectionAsString = null;
String keyAsString = null;
String valueAsString = null;
if (section != null)
{
// Unknown section value should be handled the same way as empty string
if (section.isUnknown())
{
sectionAsString = "";
}
else
{
sectionAsString = section.toStringMessage();
}
}
if (key != null)
{
// Unknown key value should be handled the same way as empty string
if (key.isUnknown())
{
keyAsString = "";
}
else
{
keyAsString = key.toStringMessage();
}
}
if (value != null)
{
// Unknown value should be handled the same way as empty string
if (value.isUnknown())
{
valueAsString = "";
}
else
{
valueAsString = value.toStringMessage();
if (valueAsString.equals("?"))
{
valueAsString = "";
}
}
}
try
{
// Using the EnvironmentAccessor interface
OSResourceManager.getEnvironments().setKeyValue(sectionAsString, keyAsString, valueAsString);
}
catch (ErrorConditionException e)
{
ErrorManager.recordOrThrowError(e.getProgressErrorCode(), e.getMessage());
}
}
/**
* Getting the default key value from the current environment.
*
* @param section
* The name of the section containing the key to get.
*
* @return The value of the key.
*/
public static character getKeyValue(character section)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
return getKeyValue(section, (character)null);
}
/**
* Getting the default key value from the current environment.
*
* @param section
* The name of the section containing the key to get.
*
* @return The value of the key.
*/
public static character getKeyValue(String section)
{
// Using null as missing parameter means the usage the default
// value for parameter, not the same as unknown. Because the
// Progress unknown value is the legal value and can be passed
// withing not-null variable instance.
return getKeyValue(new character(section), (character)null);
}
/**
* Getting the key value from the current environment.
*
* @param section
* The name of the section containing the key to get.
* @param key
* The name of the key key to get or default key if not specified.
*
* @return The value of the key.
*/
public static character getKeyValue(String section, String key)
{
return getKeyValue(new character(section), new character(key));
}
/**
* Getting the key value from the current environment.
*
* @param section
* The name of the section containing the key to get.
* @param key
* The name of the key key to get or default key if not specified.
*
* @return The value of the key.
*/
public static character getKeyValue(character section, String key)
{
return getKeyValue(section, new character(key));
}
/**
* Getting the key value from the current environment.
*
* @param section
* The name of the section containing the key to get.
* @param key
* The name of the key key to get or default key if not specified.
*
* @return The value of the key.
*/
public static character getKeyValue(String section, character key)
{
return getKeyValue(new character(section), key);
}
/**
* Getting the key value from the current environment.
*
* @param section
* The name of the section containing the key to get.
* @param key
* The name of the key key to get or default key if not specified.
*
* @return The value of the key.
*/
public static character getKeyValue(character section, character key)
{
String sectionAsString = null;
String keyAsString = null;
if (section != null)
{
// Unknown section value should be handled the same way as empty string
if (section.isUnknown())
{
sectionAsString = "";
}
else
{
sectionAsString = section.toStringMessage();
}
}
if (key != null)
{
// Unknown key value should be handled the same way as empty string
if (key.isUnknown())
{
keyAsString = "";
}
else
{
keyAsString = key.toStringMessage();
}
}
// Using the EnvironmentAccessor interface
String result = null;
try
{
result = OSResourceManager.getEnvironments().getKeyValue(sectionAsString, keyAsString);
}
catch (StopConditionException exe)
{
throw exe;
}
catch (Exception e)
{
LOG.severe("getKeyValue exception:", e);
}
return (result != null && result.isEmpty()) ? new character() : new character(result);
}
/**
* Unloads specifies environment from the current one.
*
* @param env
* The environment to unload. Can be the registry or initialization
* file. The environment to unload should be previously loaded by
* load() method.
*/
public static void unload(String env)
{
unload(new character(env));
}
/**
* Unloads specified environment from the current one if possible.
*
* @param env
* The environment to unload. Can be the registry or initialization
* file. The environment to unload should be previously loaded by
* load() method.
*/
public static void unload(character env)
{
String envName = OSResourceManager.getEnvironments().unload(env.toStringMessage());
if (envName != null)
{
onUnload(envName);
}
else
{
String msg = "The UNLOAD of " + env.toStringMessage() + " failed";
ErrorManager.recordOrThrowError(4451, msg);
}
}
/**
* Get current environment name. If no custom environment is set return default environment
* name which is a <code>null</code> value.
*
* @return see above.
*/
public static String getCurrentEnvironmentName()
{
return isCustomEnvironment() ? getEnvironmentName() : "";
}
/**
* Get client legacy platform name.
* <p>
* Sometimes we need to specify a legacy platform regarding the real platform on which the
* clients actually runs. @link ColorSpec#convert. By default the client legacy platform is
* the same as OPSYS built-in returned value in Progress.
* <p>
* The value is read from server directory. If no value is set for <code>legacy-system/opsys</code>
* then the returned value is the same as returned by the <code>getOperatingSystem</code> call.
* If set in the directory, the value of opsys MUST be UNIX or WIN32.
*
* @return Client legacy platform name:
* UNIX - Unix/Linux OS familly.
* WIN32 - Windows OS familly.
*/
public static String getLegacyPlatform()
{
WorkArea wa = work.obtain();
if (wa.legacyPlatform == null)
{
String os = getOperatingSystem().toStringMessage();
wa.legacyPlatform = Utils.getDirectoryNodeString(null, "legacy-system/opsys", os);
wa.legacyPlatformWindows = wa.legacyPlatform.toLowerCase().indexOf("win") != -1;
}
return wa.legacyPlatform;
}
/**
* Returns the word size of the client's compiled native code, which may be different
* from the "bitness" of the client's OS. For example, the client may be compiled as
* 32-bit but executed on a 64-bit OS.
*
* @return 32 or 64 depending on the compiled word size of the client's native code. If
* there is an error in calling down to the client, unknown value will be
* returned.
*/
public static integer getProcessArchitecture()
{
int arch = memptr.compiledWordSize();
// TODO: is there any case where this fails (and in 4GL it would not) so
// we introduce unknown unexpectedly?
return (arch == -1) ? new integer() : new integer(arch);
}
/**
* Returns the value of clientConfig/resolveFilePaths boolean value.
*
* @return The value of clientConfig/resolveFilePaths or false if this value isn't set.
*/
public static boolean getResolveFilePaths()
{
WorkArea wa = work.obtain();
if (wa.resolveFilePaths == null)
{
wa.resolveFilePaths = Utils.getDirectoryNodeBoolean(null,
"legacy-system/resolve-file-paths",
false);
}
return wa.resolveFilePaths;
}
/**
* Add a new listener to be notified when the PROPATH has been set.
*
* @param listener
* The listener to be notified.
*/
public static void addSourcePathListener(Consumer<String> listener)
{
WorkArea wa = work.obtain();
wa.listeners.add(listener);
}
/**
* Gets the name of the operating system upon which the Progress compatible client environment
* is running. The Java system property "os.name" of the client JVM is queried (unless running
* in headless mode, in which case that of the server JVM is used). This is then translated
* via an optional mapping table in the directory (using the same paths and precedence as
* described for the {@link #getOperatingSystem()} method, but ending in
* {@code opsys/mapping/<lowercased_system_propert_name>}.
* <p>
* If no mapping is found, then an internal mapping table in this class is consulted. This
* table maps the following:
* <p>
* <pre>
* os.name Progress OPSYS
* ---------------- ---------------
* "linux" "UNIX"
* "aix" "UNIX"
* "solaris" "UNIX"
* "sunos" "UNIX"
* "hp-ux" "UNIX"
* "freebsd" "UNIX"
* "mac os x" "UNIX"
* "windows xp" "WIN32"
* "windows 2000" "WIN32"
* "windows nt" "WIN32"
* "windows me" "WIN32"
* "windows 98" "WIN32"
* "windows 95" "WIN32"
* </pre>
* <p>
* If there is no match, then any {@code os.name} with the lowercase text "unix" will return
* "UNIX" and any {@code os.name} with the lowercase text "windows" will return "WIN32".
* <p>
* If there is still no match, the {@code os.name} returned by Java will be returned.
*
* @param wa
* Context-local work area.
*
* @return The name of the current operating system upon which the client is running.
*/
private static String getClientOpSysName(WorkArea wa)
{
String osname = null;
// query the JVM value (it will always be present)
String fallback = null;
// We have client running.
// If the client is in the headless mode (e.g. admin client), there is no physical
// terminal, so the OS can't be accessed.
if (!TransactionManager._isHeadless() && isServer())
{
fallback = OSResourceManager.getEnvironments().getOperatingSystem();
}
// We make conversion, take standalone/server value
else
{
fallback = System.getProperty("os.name");
}
// New candidate
String sysprop = fallback.toLowerCase();
// check if there is a mapping in the directory for this exact
// value
osname = "opsys/mapping/" + sysprop;
osname = Utils.getDirectoryNodeString(null, osname, "");
// use the mapping if there was one
if (osname.length() == 0)
{
// attempt to translate it by using our predefined tables
osname = (String) osMap.get(sysprop);
// no match?
if (osname == null)
{
// last try
if (sysprop.indexOf("unix") != -1)
{
osname = OS_UNIX;
}
else if (sysprop.indexOf("windows") != -1)
{
osname = OS_WIN;
}
else
{
// all else fails, return what Java specified
osname = fallback;
}
}
}
return osname;
}
/**
* A notification when <code>USE environment</code> statement is executed.
*
* @param envName
* Environment name.
*/
private static void onUse(String envName)
{
BaseSysTable.use(envName);
}
/**
* A notification when <code>UNLOAD environment</code> statement is executed.
*
* @param envName
* Environment name.
*/
private static void onUnload(String envName)
{
BaseSysTable.unload(envName);
}
/**
* Check if it's a INI file environment type.
*
* @return <code>true</code> if INI file environment type.
*/
private static boolean isIniType()
{
return EnvironmentReader.TYPE_INI.equals(getEnvironmentType());
}
/**
* Stores global data relating to the state of the current context.
*/
private static class WorkArea
{
/** Stores the current major.minor version value of the Progress compatible environment. */
private float versionNumber = 0.0f;
/** Stores the current verbose version string of the Progress compatible environment. */
private String versionVerbose = null;
/** Stores the current shortened version string of the Progress compatible environment. */
private String versionShort = null;
/** Stores the current-language of the session. */
private String currentLanguage = Utils.getDirectoryNodeString(null, "currentLanguage", "?");
/** Stores any context-specific full effective override to the <code>PROPATH</code>. */
private String propath = null;
/** Stores any context-specific override to the <code>PROPATH</code>, changeable part. */
private String propathOverride =
fixupPropath(Utils.getDirectoryNodeString(null, "searchpath", ""));
/** The batch-mode state of this context. */
private Boolean batchMode = null;
/** The FWD client log file path of this session. */
private String fwdLogfile = null;
/** The name of the OS on the client side or OPSYS override specified in the directory. */
private String clientOsName = null;
/** The name of the OS on the client side. */
private String clientRuntimeOsName = null;
/** The flag indicating Windows on client side. */
private boolean isWindowsClient;
/** The string representing delimiter between path entries on the client side. */
private String clientPathSeparator = null;
/** Client legacy platform name. */
private String legacyPlatform = null;
/** Flag indicating if the client legacy platform is Windows OS. */
private boolean legacyPlatformWindows = false;
/** Set of listeners to be notified when the {@link #propath} has been set. */
private Set<Consumer<String>> listeners = new HashSet<>();
/** Mapping of legacy absolute file path prefixes to new environment path prefixes */
private Map<String, String> pathPrefixMap = null;
/** File name case-sensitivity of the legacy platform */
private Boolean legacyCaseSensitive = null;
/** Resolve file paths on the client system */
private Boolean resolveFilePaths = null;
/** Mapping of a helper application to a mime type associated with it */
private Map<String, List<String>> supportedHelperApplications = null;
/** File name separator of the legacy platform */
private String legacyFileSeparator =
Utils.getDirectoryNodeString(null, "legacy-system/file-separator", File.separator);
/**
* Send notifications that the PROPATH has been set.
*
* @param sourcePath
* The new PROPATH value.
*/
protected void notifySourcePath(String sourcePath)
{
for (Consumer<String> pl : listeners)
{
pl.accept(sourcePath);
}
}
}
/**
* Returns <code>true</code> if the running as a server, <code>false</code> otherwise.
*
* @return See above.
*/
private static boolean isServer()
{
return SessionManager.get() != null && !SessionManager.get().isLeaf();
}
/**
* Simple container that stores and returns a context-local instance of
* the global work area.
*/
private static class ContextContainer
extends ContextLocal<WorkArea>
{
/**
* Obtains the context-local instance of the contained global work
* area.
*
* @return The work area associated with this context.
*/
public WorkArea obtain()
{
return this.get();
}
/**
* Initializes the work area, the first time it is requested within a
* new context.
*
* @return The newly instantiated work area.
*/
protected synchronized WorkArea initialValue()
{
return new WorkArea();
}
}
}