<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.obsidianscheduler.com/doc/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Craig</id>
	<title>Obsidian Scheduler - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.obsidianscheduler.com/doc/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Craig"/>
	<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/docs/Special:Contributions/Craig"/>
	<updated>2026-09-13T07:04:34Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.9</generator>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Release_Notes&amp;diff=4177</id>
		<title>Release Notes</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Release_Notes&amp;diff=4177"/>
		<updated>2026-08-12T21:58:16Z</updated>

		<summary type="html">&lt;p&gt;Craig: /* Bug Fixes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Please review our [[Upgrading_Obsidian|Upgrade Instructions]].&lt;br /&gt;
&lt;br /&gt;
Read about our [[Planned_Releases|Planned Releases]].&lt;br /&gt;
&lt;br /&gt;
Looking for old release notes? See [[Release_Notes_-_Older_Releases|Release Notes - Older Releases]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toclimit-2&amp;quot;&amp;gt;__TOC__&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Obsidian 7.0.2 ==&lt;br /&gt;
&lt;br /&gt;
Released August 2026&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Scheduler startup on JDK 17&#039;&#039;&#039; — Fixed an issue where JDK 17 required Unsafe for GSON deserialization. Unsafe is globally disabled now.&lt;br /&gt;
* &#039;&#039;&#039;Startup / shutdown job modes on Postgres and SQL Server&#039;&#039;&#039; — Job column for modes was too short for some mode values.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 7.0.1 ==&lt;br /&gt;
&lt;br /&gt;
Released August 2026&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Fixed site and hardware-linked licence verification on Obsidian 7.0.0. Workaround for 7.0.0 available on request.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 7.0.0 ==&lt;br /&gt;
&lt;br /&gt;
Released July 2026&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;OAuth 2.0 / OIDC Single Sign-On&#039;&#039;&#039; — Obsidian now supports OAuth 2.0 / OIDC SSO authentication via [[Authenticator#OAuthAuthenticator_(OIDC_SSO)|OAuthAuthenticator]]. Configure any OIDC-compliant identity provider (Keycloak, Microsoft Entra ID, Okta, Auth0, or Generic OIDC) as the login provider. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties|OAuth/OIDC Authentication Properties]] for full configuration details.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Bearer JWT authentication for the REST API&#039;&#039;&#039; — REST clients can now authenticate using a signed JWT access token in the &#039;&#039;Authorization: Bearer&#039;&#039; header. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties|OAuth/OIDC Authentication Properties]] for configuration.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Embedded Tomcat quick start&#039;&#039;&#039; — Evaluation and local runs use an Apache Tomcat 10.1.x embedded runtime (Jakarta Servlet). The bundled sample is &#039;&#039;&#039;h2-tomcat-quick-start.xml&#039;&#039;&#039;. &#039;&#039;webObsidian&#039;&#039; scripts start/stop embedded Tomcat for scheduler or admin-only profiles — see [[Installation_Guide#Deployment_options|Deployment options]] and [[Getting_Started#Starting_Obsidian_with_embedded_Tomcat|Starting Obsidian with embedded Tomcat]].&lt;br /&gt;
** On Linux, embedded Tomcat packs also install optional user-level systemd and desktop integration — see [[Installation_Guide#Linux_desktop_and_systemd_integration|the installer option]] and [[Getting_Started#Running_Obsidian_as_a_Linux_user_service|Running Obsidian as a Linux user service]].&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Global parameter autocomplete and tooltips&#039;&#039;&#039; — While editing a job, typing &#039;&#039;{{&#039;&#039; in a parameter value opens suggestions for global parameter names (subject to visibility settings). On job view, hover over values containing &#039;&#039;Name&#039;&#039; to see resolved global values when your user&#039;s permissions allow. Administration screens for defining global parameters are unchanged. See [[Advanced_Configuration|Advanced Configuration]] for visibility settings.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Install-time dependency resolution&#039;&#039;&#039; — The published installer download does not embed public open-source JARs. Libraries are fetched (or read from a pre-staged cache) when you run the installer. See [[Installation_Guide#Offline_or_restricted-network_install|Offline install]] for air-gapped preparation steps.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Installer upgrade (IzPack 5)&#039;&#039;&#039; — Obsidian 7.0.0 ships an IzPack 5-based installer. GUI and headless installs behave as in Obsidian 6 for standard flows. Customers with saved Obsidian 6 automated install XML must run the migrate helper included in the download before headless install. See [[Upgrading_Obsidian#Migrating_saved_automated_install_XML_.28Obsidian_6_.E2.86.92_7.29|Upgrading Obsidian]].&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Modular Groovy and Flexmark libraries&#039;&#039;&#039; — Obsidian no longer ships &#039;&#039;&#039;apache-groovy-all-4.0.24.jar&#039;&#039;&#039; or &#039;&#039;&#039;flexmark-util-0.62.2.jar&#039;&#039;&#039; uber archives. Scripting and UI documentation rendering use the modular JAR sets documented in [[Advanced_Configuration#Dependent_Libraries|Dependent Libraries]].&lt;br /&gt;
&lt;br /&gt;
=== Removals ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Remember Me login option removed&#039;&#039;&#039; — The Remember Me checkbox and associated session-cookie mechanism have been removed. The &#039;&#039;USER_COOKIE&#039;&#039; table and &#039;&#039;allowRememberMe&#039;&#039; system parameter are removed automatically during the upgrade to 7.0.0; no operator action is required.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;User logout&#039;&#039;&#039; - Fixed an issue where users were not fully logged out in certain session states.&lt;br /&gt;
* &#039;&#039;&#039;Job edit screen accessible to users with no roles&#039;&#039;&#039; — Users with no assigned roles could previously access the job edit screen but not make changes; this has been fixed.&lt;br /&gt;
* &#039;&#039;&#039;Firefox UI icons&#039;&#039;&#039; — Fixed icons not rendering correctly in Firefox.&lt;br /&gt;
* Jakarta emails sent with both text and HTML would drop HTML portion; this has been fixed.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.4.0 ==&lt;br /&gt;
Released December 2025&lt;br /&gt;
&lt;br /&gt;
=== Enhancements === &lt;br /&gt;
* New [[Built-in_Jobs#Obsidian_Execution_Statistics_Job|Execution Statistics Job]] and built in [[Admin_Job_Stats|UI]] and [[Embedded_API#List_Stats|Embedded API]] and [[REST_Endpoints#GET_a_list_of_job_execution_statistics|REST API]]&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Fixed issue preventing [[Admin_Jobs#Deleting|job deletion]] when resubmissions of failed executions existed. Bug existed in UI and APIs.&lt;br /&gt;
* Fixed UI styling issue for [[Admin_Jobs#Deleting|job deletion]] where checkbox label did not appear.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.3.3 ==&lt;br /&gt;
Released November 2025&lt;br /&gt;
&lt;br /&gt;
=== Enhancements === &lt;br /&gt;
* [https://web.obsidianscheduler.com/obsidianapi/com/carfey/suite/security/LdapAuthenticator.html LDAPAuthenticator] supports configurable check user active override - com.carfey.suite.security.LdapAuthenticator.checkActiveAttribute&lt;br /&gt;
* [https://web.obsidianscheduler.com/obsidianapi/com/carfey/suite/security/LdapAuthenticator.html#isUserActive(javax.naming.directory.DirContext,java.lang.String) LDAPAuthenticator.isUserActive(DirContext,String)] visibility increased to protected to allow for functionality override in subclasses.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Correct LdapAuthenticator case with delimited configuration where active check wasn&#039;t applied.&lt;br /&gt;
* [https://web.obsidianscheduler.com/obsidianapi/com/carfey/suite/security/LdapAuthenticator.html#isUserActive(javax.naming.directory.DirContext,java.lang.String) LDAPAuthenticator.isUserActive(DirContext,String)] javadoc added for isUserActive fully detailing the implementation.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.3.2 ==&lt;br /&gt;
Released October 2025&lt;br /&gt;
&lt;br /&gt;
=== Enhancements === &lt;br /&gt;
* Notification failures now trigger Dispatch category Error level event on first failure. For use in log alerts and Event Hooks.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Notification failures no longer log on every failure reducing chatty failure logs.&lt;br /&gt;
* No-arg constructors added to multiple JSON serializable candidate POJOs missing them. Addresses GSON&#039;s workaround use of sun.misc.Unsafe.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.3.1 ==&lt;br /&gt;
Released August 2025&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Fix failure response codes on [[REST_Endpoints#GET_health_details_on_an_existing_scheduling_host|REST health endpoint]]&lt;br /&gt;
* Fix automatic upgrade issue from 6.1.0 to any of 6.2.0, 6.2.1, 6.3.0&lt;br /&gt;
* Fix auth issue when [[Admin_User_Management#Multi-Factor_Authentication_.28MFA.29|MFA]] is enabled that allows REST access when account has been locked out due to MFA not being setup.  [[Contact_the_Obsidian_Scheduler_Team|Contact us]] if you need workaround details for earlier versions.&lt;br /&gt;
* Fix native auth issue that allows REST access when account has been flagged as inactive.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.3.0 ==&lt;br /&gt;
Released July 2025&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* Cron day of month expansion for [[Cron#Special_Character_Usage|day of week proximity]] using [[Cron#Examples|~]]&lt;br /&gt;
* Annotation based job and chain configuration [[Initializing_and_Restoring#Annotation_Initialization|initialization]]&lt;br /&gt;
* Health endpoint with details for Job Queuer and Job Spawner - [[REST_Endpoints#GET_health_details_on_an_existing_scheduling_host|REST API]] or Embedded API [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/HostManager.html#getHealth(java.lang.String) by hostname] or [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/HostManager.html#getHealth(long) by host id]&lt;br /&gt;
* JDBC URL construction from parts [[Advanced_Configuration#Database_Properties|host/port/databaseName/dbType/oracleSid]]&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.2.1 ==&lt;br /&gt;
Released June 2025&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Certain recoveries greater than 24 hours using new day of month proximity patterns introduced in 6.2.0 would result in no jobs spawning.&lt;br /&gt;
* Migrations targeting 6.0.0 and greater that start earlier than 5.0.0 skip the 5.0.0 migration. Migrating to any 5.x version first and then to 6.0.0 or later works around this issue.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.2.0 ==&lt;br /&gt;
Released May 2025&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* Cron day of month expansion for [[Cron#Special_Character_Usage|day of week proximity]] using [[Cron#Examples|&amp;lt; &amp;gt; ≥ ≤]]&lt;br /&gt;
* Cron day of month expansion for [[Cron#Special_Character_Usage|weekday proximity]] using [[Cron#Examples|&amp;lt; &amp;gt; ≥ ≤]]&lt;br /&gt;
* [[Admin_Global_Parameters#Using_Global_Parameters_in_Jobs|Global Parameters]] reference in job edit page support mouseover/title display of actual value when permissions allow&lt;br /&gt;
* Host time displayed in [[Admin_Host_Status|hosts status]] in UI&lt;br /&gt;
* [[Admin_Job_Runtime_Preview|Runtime Preview]] optimizations for large date ranges in both UI and APIs.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Combination of table prefix, sorting by clob column and Oracle metadata load failure no longer results in sql error. This occurred in the UI when viewing raw job history results and in API calls for job runtime results.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.1.1 ==&lt;br /&gt;
Released March 2025&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* [[Authenticator#Implementation_of_a_Custom_Authenticator|Authenticator]] supports optional &amp;lt;code&amp;gt;authenticateREST()&amp;lt;/code&amp;gt; method.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Native authentication REST logins do not apply &#039;last login&#039; timestamps. Avoids noisy error that was appearing in logs.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.1.0 ==&lt;br /&gt;
Released March 2025&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* Native authentication now disables users after 4 invalid login attempts, either password or MFA code if applicable. Invalid attempts threshold is configurable.&lt;br /&gt;
* Native authentication last login datetime displayed on list user screen.&lt;br /&gt;
* Stronger licensing controls. Site and hardware licenses as of this version must be regenerated by Carfey Software and every 2 years. Contact us at licensing [[Image:atSymbol.png]] obsidianscheduler.com.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Native authentication password complexity pattern properly created and no longer overwritten on restart.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.0.1 ==&lt;br /&gt;
Released February 2025&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* [[Built-in_Jobs#File_Archive_Job|File Archive Job]] and [[Built-in_Jobs#File_Scanner_Job|File Scanner Job]] both now support min/max file sizes and minimum age.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Native authentication user updates now support long passwords.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.0.0 ==&lt;br /&gt;
Released December 27, 2024&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* Jakarta Servlet 5.0&lt;br /&gt;
** Servlet container for Web Admin must support Jakarta Servlet 5.0 (e.g. Tomcat 10, Jetty 11)&lt;br /&gt;
* [[Micronaut_Integration|Micronaut]] integration support&lt;br /&gt;
* [[Advanced_Configuration#Dependent_Libraries|Groovy 4]] support&lt;br /&gt;
* [[Advanced_Configuration#Properties.2FYaml_File|Yaml]] configuration support&amp;lt;ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using automated installer files from previous releases, you will need to add an additional configuration item to choose between yaml and properties formats in UserInputPanel.0.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;com.izforge.izpack.panels.UserInputPanel id=&amp;quot;UserInputPanel.0&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;userInput&amp;gt;&lt;br /&gt;
......snip......&lt;br /&gt;
            &amp;lt;entry key=&amp;quot;config.format&amp;quot; value=&amp;quot;yaml&amp;quot; /&amp;gt;&lt;br /&gt;
OR&lt;br /&gt;
            &amp;lt;entry key=&amp;quot;config.format&amp;quot; value=&amp;quot;properties&amp;quot; /&amp;gt;&lt;br /&gt;
......snip......&lt;br /&gt;
        &amp;lt;/userInput&amp;gt;&lt;br /&gt;
    &amp;lt;/com.izforge.izpack.panels.UserInputPanel&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/ref&amp;gt;&lt;br /&gt;
* Native authentication supports customizable [[Admin_Login#Password_Complexity|password complexity]] requirements&lt;br /&gt;
* XML support deprecated across the product including XML UI downloads, XML runner configurations and XML license leases.&lt;br /&gt;
** &#039;&#039;&#039;Starting January 1st 2027, XML license lease requests will stop being processed. All Obsidian instances running using internet-verified licenses (including licence key proxies) will be required to use release 6.0.0 or later as of January 1st 2027.&#039;&#039;&#039;&lt;br /&gt;
** Above noted XML support will be removed in the first Obsidian version released in 2027.&lt;br /&gt;
* License leases use JSON payloads.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.5.1 ==&lt;br /&gt;
Released December 16, 2024&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Admin only (no scheduler) UI no longer generates event hook errors while running nor during shutdown&lt;br /&gt;
* Quick start installer file no longer generates errors during installation&lt;br /&gt;
* A few small web UI enhancements&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.5.0 ==&lt;br /&gt;
Released October 2024&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* Event Hooks management available in [[Admin_Host_Status#Event_Hook_Status|UI]], [[Embedded_API#Event_Hook_Resume_or_Pause|Embedded API]] and [[REST_Endpoints#POST_event_hook_pause_or_resume|REST API]].&lt;br /&gt;
* [[Installation_Guide#Additional_configuration_items|Installer]] supports custom add on configurations    &#039;&#039;&#039;Potential breaking change to automated installer files.&#039;&#039;&#039; &amp;lt;ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using automated installer files from previous releases, you will need to add a new section of xml as of Obsidian 5.5.0 to handle a new UserInputPanel. Immediately after the UserInputPanel.17 closing brace, add the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;com.izforge.izpack.panels.UserInputPanel id=&amp;quot;UserInputPanel.18&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;userInput&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.1&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.2&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.3&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.4&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.5&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.6&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.7&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.8&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.9&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.10&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.11&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.12&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.13&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.14&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.15&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.16&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.17&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.18&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.19&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.20&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.1&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.2&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.3&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.4&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.5&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.6&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.7&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.8&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.9&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.11&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.10&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.12&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.13&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.14&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.15&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.16&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.17&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.18&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.19&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.20&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
	&amp;lt;/userInput&amp;gt;&lt;br /&gt;
&amp;lt;/com.izforge.izpack.panels.UserInputPanel&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Certain Cron expressions that fail to generate text descriptions no longer impact scheduling.&lt;br /&gt;
* Text database columns were previously restricted to maximum length of MySQL implementation. Corrected to validate length via DB implementation.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.4.0 ==&lt;br /&gt;
Released June 2024&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* Event Hooks available in [[Embedded_API#List_Event_Hooks|Embedded API]] and [[REST_Endpoints#GET_event_hooks|REST API]].&lt;br /&gt;
* [[Advanced_Configuration#Dependent_Libraries|GSON library]] upgrade to support Java 21&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Oracle identifier no longer too long when using prefixes.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.3.0 ==&lt;br /&gt;
Released March 2024&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* New [[Admin_Host_Status#Event_Hook_Status| Event Hooks Status]] available in the UI.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Some improvements throughout the [[Admin_Web_Application_Guide|Admin Web Application]] for autofocus of fields.&lt;br /&gt;
* Additional classes and interfaces added to [https://web.obsidianscheduler.com/obsidianapi/ javadoc].&lt;br /&gt;
* Some cleanup in [https://web.obsidianscheduler.com/obsidianapi/ javadoc] documenation.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.2.1 == &lt;br /&gt;
Released January 2024&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* [[Built-in_Jobs#Maintenance_Jobs|Maintenance Jobs]] are now scheduled by default in new installations. Can be disabled via [[Advanced_Configuration#Miscellaneous_Properties|Configuration]] property.&lt;br /&gt;
* New [[Event_Hooks#Standard_Output.2FError_Streams_Event_Hook | Standard Output/Error Streams Event Hook]].&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Schedule descriptions are now updated after edits are applied in all UI screens and APIs.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.2.0 ==&lt;br /&gt;
Released December 2023&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* [[Cron]] &amp;amp; [[Cron#Recurrence|Recur]] patterns along with any use of [[Admin_Schedule_Aliases|Schedule Aliases]] now support plain language description of the patterns throughout the [[Admin_Web_Application_Guide|UI]], visible while hovering patterns, and in [[REST_Endpoints|REST]] and [[Embedded_API|Embedded]] API responses.&lt;br /&gt;
* All screens supporting UI exports now support JSON ([[Admin_Job_Activity#Exporting_Results|Job Activity]], [[Admin_Jobs#Exporting_Results|Jobs]], [[Admin_Job_Runtime_Preview#Exporting_Results|Runtime Previews]], [[Admin_Job_Chains#Job_Chain_Listing|Job Chains]], [[Admin_Logs#Exporting_Results|Logs]], [[Admin_Notifications#Exporting_Results|Sent Notifications]], [[Admin_User_Management#Exporting_Results|Users]], [[Admin_Custom_Calendars#Calendar_Listing|Calendars]])&lt;br /&gt;
* All screens supporting UI exports and search criteria and/or inline filters now include any specified search criteria and filter text in Excel, XML and JSON downloads ([[Admin_Job_Activity#Exporting_Results|Job Activity]], [[Admin_Jobs#Exporting_Results|Jobs]], [[Admin_Job_Runtime_Preview#Exporting_Results|Runtime Previews]], [[Admin_Job_Chains#Job_Chain_Listing|Job Chains]], [[Admin_Logs#Exporting_Results|Logs]], [[Admin_Notifications#Exporting_Results|Sent Notifications]], [[Admin_User_Management#Exporting_Results|Users]])&lt;br /&gt;
* Support for [[Installation_Guide#Choosing_Email_Support|Jakarta EE mail]] &#039;&#039;&#039;Potential breaking change to automated installer files.&#039;&#039;&#039; &amp;lt;ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using automated installer files from previous releases and had email configured, you will need to add a new section of xml as of Obsidian 5.2.0 to handle a new UserInputPanel. Immediately after the UserInputPanel.16 closing brace, add the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;com.izforge.izpack.panels.UserInputPanel id=&amp;quot;UserInputPanel.17&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;userInput&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;mail.type.selection&amp;quot; value=&amp;quot;javax&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;/userInput&amp;gt;&lt;br /&gt;
&amp;lt;/com.izforge.izpack.panels.UserInputPanel&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/ref&amp;gt;&lt;br /&gt;
* New [[Event_Hooks#REST_Endpoint_Event_Hook|REST Endpoint Event Hook]]&lt;br /&gt;
* [[Key_Server_Proxy|Key Server proxy]] artifact obtained via web download during installation&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Time picker buttons (hour, minute, AM/PM) in [[Admin_Job_Activity#Filtering|Job Activity filtering]] no longer change other elements of the selected time.&lt;br /&gt;
* Cron pattern with [[Cron#Special_Character_Usage|LW]] and any other non-L value in day position no longer also incorrectly evaluates to last day.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.1.1 ==&lt;br /&gt;
Released June 2023&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* [[Authenticator|Native authentication]] no longer fails when user deletes are attempted from the UI.&lt;br /&gt;
* Built in maintenance job [[Built-in_Jobs#Job_History_Cleanup_Job|Job History Cleanup]] no longer leaves deletion candidate CHAIN SKIPPED records in the JOB_HISTORY table in rare circumstances.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.1.0 ==&lt;br /&gt;
Released April 2023&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* [[Admin_Schedule_Aliases#Schedule_Alias_Fragments|Schedule Aliases]] now support fragments for configuration-time substitutions.&lt;br /&gt;
* New convenience job [[Built-in_Jobs#Database_File_Export_Job|Database File Export Job]] for generating basic file extracts from database queries.&lt;br /&gt;
* New convenience job [[Built-in_Jobs#REST_Invocation_Job|REST Invocation Job]] for making simple REST calls and storing results.&lt;br /&gt;
* Performance improvements in job failure handling.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.0.4 == &lt;br /&gt;
Released February 2022&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Log4j2 2.17.1 as fix for [https://logging.apache.org/log4j/2.x/security.html#log4j-2.17.1 RCE vulnerability] where attackers can modify log4j configuration.&lt;br /&gt;
* Restore missing default log4j2 configuration in installation artifacts.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.0.3 == &lt;br /&gt;
Released December 2021&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Log4j2 2.17.0 as fix for [https://logging.apache.org/log4j/2.x/security.html#log4j-2.17.0 DOS vulnerability]&lt;br /&gt;
* Fix native login issue showing as inactive on some databases.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.0.2 == &lt;br /&gt;
Released December 2021&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Log4j2 2.16.0 as permanent fix for [https://logging.apache.org/log4j/2.x/security.html#log4j-2.16.0 RCE vulnerability]&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.0.1 == &lt;br /&gt;
Released December 2021&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Log4j2 2.15.0 as fix for [https://logging.apache.org/log4j/2.x/security.html#log4j-2.15.0 RCE vulnerability]&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
* Fix sporadic native login issue on some databases.&lt;br /&gt;
* Formatting fix in quick installer file&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.0.0 == &lt;br /&gt;
Released August 2021.&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Java 11 (minimum Java version)&lt;br /&gt;
* [[Admin_Schedule_Aliases|Schedule Aliases]] including support in [[REST_Endpoints#Schedule_Alias_Endpoints|REST API]] and [[Embedded_API#ScheduleAliasManager_API|Embedded API]]&lt;br /&gt;
* [[Admin_User_Management#Multi-Factor_Authentication_.28MFA.29|MFA Support]] for UI logins&lt;br /&gt;
* New [[Admin_User_Management#User_Rights|Author and Operator]] roles&lt;br /&gt;
* Convention-based role permissions by [[Admin_User_Management#Job_Folder_Rights|root job folder]] for Write, Author and Operator.&amp;lt;ref&amp;gt;&lt;br /&gt;
There is a possibility of a breaking change to Embedded or REST API use due to the need to change the [[Embedded_API#Enumerations|User Role enumeration]] from a Java enum to an enum-style class to support this feature. Bringing in the upgraded Obsidian library and compiling should reveal any such broken use of these enumerations. Needed changes should be minor and self-explanatory.&amp;lt;/ref&amp;gt;&lt;br /&gt;
* Bundled Jetty 10.0.2&lt;br /&gt;
* Legacy Embedded API (from Obsidian 1.5) dropped&lt;br /&gt;
* Signal handler disabled by default. Enabled only via [[Advanced_Configuration#Miscellaneous_Properties|configuration]].&lt;br /&gt;
* Many [[Advanced_Configuration#Dependent_Libraries|library upgrades]].&lt;br /&gt;
* UI javascript library updates.&lt;br /&gt;
&lt;br /&gt;
== Footnotes ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Release_Notes&amp;diff=4176</id>
		<title>Release Notes</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Release_Notes&amp;diff=4176"/>
		<updated>2026-08-12T21:57:42Z</updated>

		<summary type="html">&lt;p&gt;Craig: Wiki update&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Please review our [[Upgrading_Obsidian|Upgrade Instructions]].&lt;br /&gt;
&lt;br /&gt;
Read about our [[Planned_Releases|Planned Releases]].&lt;br /&gt;
&lt;br /&gt;
Looking for old release notes? See [[Release_Notes_-_Older_Releases|Release Notes - Older Releases]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toclimit-2&amp;quot;&amp;gt;__TOC__&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Obsidian 7.0.2 ==&lt;br /&gt;
&lt;br /&gt;
Released August 2026&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Scheduler startup on JDK 17&#039;&#039;&#039; — Fixed an issue where JDK 17 required Unsafe for GSON deserialization. Unsafe is globally disabled now.&lt;br /&gt;
* &#039;&#039;&#039;Startup / shutdown job modes on Postgres and SQL Server&#039;&#039;&#039; — Job column for modes was too short some values.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 7.0.1 ==&lt;br /&gt;
&lt;br /&gt;
Released August 2026&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Fixed site and hardware-linked licence verification on Obsidian 7.0.0. Workaround for 7.0.0 available on request.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 7.0.0 ==&lt;br /&gt;
&lt;br /&gt;
Released July 2026&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;OAuth 2.0 / OIDC Single Sign-On&#039;&#039;&#039; — Obsidian now supports OAuth 2.0 / OIDC SSO authentication via [[Authenticator#OAuthAuthenticator_(OIDC_SSO)|OAuthAuthenticator]]. Configure any OIDC-compliant identity provider (Keycloak, Microsoft Entra ID, Okta, Auth0, or Generic OIDC) as the login provider. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties|OAuth/OIDC Authentication Properties]] for full configuration details.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Bearer JWT authentication for the REST API&#039;&#039;&#039; — REST clients can now authenticate using a signed JWT access token in the &#039;&#039;Authorization: Bearer&#039;&#039; header. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties|OAuth/OIDC Authentication Properties]] for configuration.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Embedded Tomcat quick start&#039;&#039;&#039; — Evaluation and local runs use an Apache Tomcat 10.1.x embedded runtime (Jakarta Servlet). The bundled sample is &#039;&#039;&#039;h2-tomcat-quick-start.xml&#039;&#039;&#039;. &#039;&#039;webObsidian&#039;&#039; scripts start/stop embedded Tomcat for scheduler or admin-only profiles — see [[Installation_Guide#Deployment_options|Deployment options]] and [[Getting_Started#Starting_Obsidian_with_embedded_Tomcat|Starting Obsidian with embedded Tomcat]].&lt;br /&gt;
** On Linux, embedded Tomcat packs also install optional user-level systemd and desktop integration — see [[Installation_Guide#Linux_desktop_and_systemd_integration|the installer option]] and [[Getting_Started#Running_Obsidian_as_a_Linux_user_service|Running Obsidian as a Linux user service]].&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Global parameter autocomplete and tooltips&#039;&#039;&#039; — While editing a job, typing &#039;&#039;{{&#039;&#039; in a parameter value opens suggestions for global parameter names (subject to visibility settings). On job view, hover over values containing &#039;&#039;Name&#039;&#039; to see resolved global values when your user&#039;s permissions allow. Administration screens for defining global parameters are unchanged. See [[Advanced_Configuration|Advanced Configuration]] for visibility settings.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Install-time dependency resolution&#039;&#039;&#039; — The published installer download does not embed public open-source JARs. Libraries are fetched (or read from a pre-staged cache) when you run the installer. See [[Installation_Guide#Offline_or_restricted-network_install|Offline install]] for air-gapped preparation steps.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Installer upgrade (IzPack 5)&#039;&#039;&#039; — Obsidian 7.0.0 ships an IzPack 5-based installer. GUI and headless installs behave as in Obsidian 6 for standard flows. Customers with saved Obsidian 6 automated install XML must run the migrate helper included in the download before headless install. See [[Upgrading_Obsidian#Migrating_saved_automated_install_XML_.28Obsidian_6_.E2.86.92_7.29|Upgrading Obsidian]].&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Modular Groovy and Flexmark libraries&#039;&#039;&#039; — Obsidian no longer ships &#039;&#039;&#039;apache-groovy-all-4.0.24.jar&#039;&#039;&#039; or &#039;&#039;&#039;flexmark-util-0.62.2.jar&#039;&#039;&#039; uber archives. Scripting and UI documentation rendering use the modular JAR sets documented in [[Advanced_Configuration#Dependent_Libraries|Dependent Libraries]].&lt;br /&gt;
&lt;br /&gt;
=== Removals ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Remember Me login option removed&#039;&#039;&#039; — The Remember Me checkbox and associated session-cookie mechanism have been removed. The &#039;&#039;USER_COOKIE&#039;&#039; table and &#039;&#039;allowRememberMe&#039;&#039; system parameter are removed automatically during the upgrade to 7.0.0; no operator action is required.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;User logout&#039;&#039;&#039; - Fixed an issue where users were not fully logged out in certain session states.&lt;br /&gt;
* &#039;&#039;&#039;Job edit screen accessible to users with no roles&#039;&#039;&#039; — Users with no assigned roles could previously access the job edit screen but not make changes; this has been fixed.&lt;br /&gt;
* &#039;&#039;&#039;Firefox UI icons&#039;&#039;&#039; — Fixed icons not rendering correctly in Firefox.&lt;br /&gt;
* Jakarta emails sent with both text and HTML would drop HTML portion; this has been fixed.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.4.0 ==&lt;br /&gt;
Released December 2025&lt;br /&gt;
&lt;br /&gt;
=== Enhancements === &lt;br /&gt;
* New [[Built-in_Jobs#Obsidian_Execution_Statistics_Job|Execution Statistics Job]] and built in [[Admin_Job_Stats|UI]] and [[Embedded_API#List_Stats|Embedded API]] and [[REST_Endpoints#GET_a_list_of_job_execution_statistics|REST API]]&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Fixed issue preventing [[Admin_Jobs#Deleting|job deletion]] when resubmissions of failed executions existed. Bug existed in UI and APIs.&lt;br /&gt;
* Fixed UI styling issue for [[Admin_Jobs#Deleting|job deletion]] where checkbox label did not appear.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.3.3 ==&lt;br /&gt;
Released November 2025&lt;br /&gt;
&lt;br /&gt;
=== Enhancements === &lt;br /&gt;
* [https://web.obsidianscheduler.com/obsidianapi/com/carfey/suite/security/LdapAuthenticator.html LDAPAuthenticator] supports configurable check user active override - com.carfey.suite.security.LdapAuthenticator.checkActiveAttribute&lt;br /&gt;
* [https://web.obsidianscheduler.com/obsidianapi/com/carfey/suite/security/LdapAuthenticator.html#isUserActive(javax.naming.directory.DirContext,java.lang.String) LDAPAuthenticator.isUserActive(DirContext,String)] visibility increased to protected to allow for functionality override in subclasses.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Correct LdapAuthenticator case with delimited configuration where active check wasn&#039;t applied.&lt;br /&gt;
* [https://web.obsidianscheduler.com/obsidianapi/com/carfey/suite/security/LdapAuthenticator.html#isUserActive(javax.naming.directory.DirContext,java.lang.String) LDAPAuthenticator.isUserActive(DirContext,String)] javadoc added for isUserActive fully detailing the implementation.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.3.2 ==&lt;br /&gt;
Released October 2025&lt;br /&gt;
&lt;br /&gt;
=== Enhancements === &lt;br /&gt;
* Notification failures now trigger Dispatch category Error level event on first failure. For use in log alerts and Event Hooks.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Notification failures no longer log on every failure reducing chatty failure logs.&lt;br /&gt;
* No-arg constructors added to multiple JSON serializable candidate POJOs missing them. Addresses GSON&#039;s workaround use of sun.misc.Unsafe.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.3.1 ==&lt;br /&gt;
Released August 2025&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Fix failure response codes on [[REST_Endpoints#GET_health_details_on_an_existing_scheduling_host|REST health endpoint]]&lt;br /&gt;
* Fix automatic upgrade issue from 6.1.0 to any of 6.2.0, 6.2.1, 6.3.0&lt;br /&gt;
* Fix auth issue when [[Admin_User_Management#Multi-Factor_Authentication_.28MFA.29|MFA]] is enabled that allows REST access when account has been locked out due to MFA not being setup.  [[Contact_the_Obsidian_Scheduler_Team|Contact us]] if you need workaround details for earlier versions.&lt;br /&gt;
* Fix native auth issue that allows REST access when account has been flagged as inactive.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.3.0 ==&lt;br /&gt;
Released July 2025&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* Cron day of month expansion for [[Cron#Special_Character_Usage|day of week proximity]] using [[Cron#Examples|~]]&lt;br /&gt;
* Annotation based job and chain configuration [[Initializing_and_Restoring#Annotation_Initialization|initialization]]&lt;br /&gt;
* Health endpoint with details for Job Queuer and Job Spawner - [[REST_Endpoints#GET_health_details_on_an_existing_scheduling_host|REST API]] or Embedded API [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/HostManager.html#getHealth(java.lang.String) by hostname] or [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/HostManager.html#getHealth(long) by host id]&lt;br /&gt;
* JDBC URL construction from parts [[Advanced_Configuration#Database_Properties|host/port/databaseName/dbType/oracleSid]]&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.2.1 ==&lt;br /&gt;
Released June 2025&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Certain recoveries greater than 24 hours using new day of month proximity patterns introduced in 6.2.0 would result in no jobs spawning.&lt;br /&gt;
* Migrations targeting 6.0.0 and greater that start earlier than 5.0.0 skip the 5.0.0 migration. Migrating to any 5.x version first and then to 6.0.0 or later works around this issue.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.2.0 ==&lt;br /&gt;
Released May 2025&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* Cron day of month expansion for [[Cron#Special_Character_Usage|day of week proximity]] using [[Cron#Examples|&amp;lt; &amp;gt; ≥ ≤]]&lt;br /&gt;
* Cron day of month expansion for [[Cron#Special_Character_Usage|weekday proximity]] using [[Cron#Examples|&amp;lt; &amp;gt; ≥ ≤]]&lt;br /&gt;
* [[Admin_Global_Parameters#Using_Global_Parameters_in_Jobs|Global Parameters]] reference in job edit page support mouseover/title display of actual value when permissions allow&lt;br /&gt;
* Host time displayed in [[Admin_Host_Status|hosts status]] in UI&lt;br /&gt;
* [[Admin_Job_Runtime_Preview|Runtime Preview]] optimizations for large date ranges in both UI and APIs.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Combination of table prefix, sorting by clob column and Oracle metadata load failure no longer results in sql error. This occurred in the UI when viewing raw job history results and in API calls for job runtime results.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.1.1 ==&lt;br /&gt;
Released March 2025&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* [[Authenticator#Implementation_of_a_Custom_Authenticator|Authenticator]] supports optional &amp;lt;code&amp;gt;authenticateREST()&amp;lt;/code&amp;gt; method.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Native authentication REST logins do not apply &#039;last login&#039; timestamps. Avoids noisy error that was appearing in logs.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.1.0 ==&lt;br /&gt;
Released March 2025&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* Native authentication now disables users after 4 invalid login attempts, either password or MFA code if applicable. Invalid attempts threshold is configurable.&lt;br /&gt;
* Native authentication last login datetime displayed on list user screen.&lt;br /&gt;
* Stronger licensing controls. Site and hardware licenses as of this version must be regenerated by Carfey Software and every 2 years. Contact us at licensing [[Image:atSymbol.png]] obsidianscheduler.com.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Native authentication password complexity pattern properly created and no longer overwritten on restart.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.0.1 ==&lt;br /&gt;
Released February 2025&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* [[Built-in_Jobs#File_Archive_Job|File Archive Job]] and [[Built-in_Jobs#File_Scanner_Job|File Scanner Job]] both now support min/max file sizes and minimum age.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Native authentication user updates now support long passwords.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.0.0 ==&lt;br /&gt;
Released December 27, 2024&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* Jakarta Servlet 5.0&lt;br /&gt;
** Servlet container for Web Admin must support Jakarta Servlet 5.0 (e.g. Tomcat 10, Jetty 11)&lt;br /&gt;
* [[Micronaut_Integration|Micronaut]] integration support&lt;br /&gt;
* [[Advanced_Configuration#Dependent_Libraries|Groovy 4]] support&lt;br /&gt;
* [[Advanced_Configuration#Properties.2FYaml_File|Yaml]] configuration support&amp;lt;ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using automated installer files from previous releases, you will need to add an additional configuration item to choose between yaml and properties formats in UserInputPanel.0.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;com.izforge.izpack.panels.UserInputPanel id=&amp;quot;UserInputPanel.0&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;userInput&amp;gt;&lt;br /&gt;
......snip......&lt;br /&gt;
            &amp;lt;entry key=&amp;quot;config.format&amp;quot; value=&amp;quot;yaml&amp;quot; /&amp;gt;&lt;br /&gt;
OR&lt;br /&gt;
            &amp;lt;entry key=&amp;quot;config.format&amp;quot; value=&amp;quot;properties&amp;quot; /&amp;gt;&lt;br /&gt;
......snip......&lt;br /&gt;
        &amp;lt;/userInput&amp;gt;&lt;br /&gt;
    &amp;lt;/com.izforge.izpack.panels.UserInputPanel&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/ref&amp;gt;&lt;br /&gt;
* Native authentication supports customizable [[Admin_Login#Password_Complexity|password complexity]] requirements&lt;br /&gt;
* XML support deprecated across the product including XML UI downloads, XML runner configurations and XML license leases.&lt;br /&gt;
** &#039;&#039;&#039;Starting January 1st 2027, XML license lease requests will stop being processed. All Obsidian instances running using internet-verified licenses (including licence key proxies) will be required to use release 6.0.0 or later as of January 1st 2027.&#039;&#039;&#039;&lt;br /&gt;
** Above noted XML support will be removed in the first Obsidian version released in 2027.&lt;br /&gt;
* License leases use JSON payloads.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.5.1 ==&lt;br /&gt;
Released December 16, 2024&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Admin only (no scheduler) UI no longer generates event hook errors while running nor during shutdown&lt;br /&gt;
* Quick start installer file no longer generates errors during installation&lt;br /&gt;
* A few small web UI enhancements&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.5.0 ==&lt;br /&gt;
Released October 2024&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* Event Hooks management available in [[Admin_Host_Status#Event_Hook_Status|UI]], [[Embedded_API#Event_Hook_Resume_or_Pause|Embedded API]] and [[REST_Endpoints#POST_event_hook_pause_or_resume|REST API]].&lt;br /&gt;
* [[Installation_Guide#Additional_configuration_items|Installer]] supports custom add on configurations    &#039;&#039;&#039;Potential breaking change to automated installer files.&#039;&#039;&#039; &amp;lt;ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using automated installer files from previous releases, you will need to add a new section of xml as of Obsidian 5.5.0 to handle a new UserInputPanel. Immediately after the UserInputPanel.17 closing brace, add the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;com.izforge.izpack.panels.UserInputPanel id=&amp;quot;UserInputPanel.18&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;userInput&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.1&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.2&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.3&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.4&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.5&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.6&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.7&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.8&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.9&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.10&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.11&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.12&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.13&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.14&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.15&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.16&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.17&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.18&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.19&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.20&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.1&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.2&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.3&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.4&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.5&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.6&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.7&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.8&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.9&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.11&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.10&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.12&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.13&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.14&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.15&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.16&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.17&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.18&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.19&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.20&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
	&amp;lt;/userInput&amp;gt;&lt;br /&gt;
&amp;lt;/com.izforge.izpack.panels.UserInputPanel&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Certain Cron expressions that fail to generate text descriptions no longer impact scheduling.&lt;br /&gt;
* Text database columns were previously restricted to maximum length of MySQL implementation. Corrected to validate length via DB implementation.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.4.0 ==&lt;br /&gt;
Released June 2024&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* Event Hooks available in [[Embedded_API#List_Event_Hooks|Embedded API]] and [[REST_Endpoints#GET_event_hooks|REST API]].&lt;br /&gt;
* [[Advanced_Configuration#Dependent_Libraries|GSON library]] upgrade to support Java 21&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Oracle identifier no longer too long when using prefixes.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.3.0 ==&lt;br /&gt;
Released March 2024&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* New [[Admin_Host_Status#Event_Hook_Status| Event Hooks Status]] available in the UI.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Some improvements throughout the [[Admin_Web_Application_Guide|Admin Web Application]] for autofocus of fields.&lt;br /&gt;
* Additional classes and interfaces added to [https://web.obsidianscheduler.com/obsidianapi/ javadoc].&lt;br /&gt;
* Some cleanup in [https://web.obsidianscheduler.com/obsidianapi/ javadoc] documenation.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.2.1 == &lt;br /&gt;
Released January 2024&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* [[Built-in_Jobs#Maintenance_Jobs|Maintenance Jobs]] are now scheduled by default in new installations. Can be disabled via [[Advanced_Configuration#Miscellaneous_Properties|Configuration]] property.&lt;br /&gt;
* New [[Event_Hooks#Standard_Output.2FError_Streams_Event_Hook | Standard Output/Error Streams Event Hook]].&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Schedule descriptions are now updated after edits are applied in all UI screens and APIs.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.2.0 ==&lt;br /&gt;
Released December 2023&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* [[Cron]] &amp;amp; [[Cron#Recurrence|Recur]] patterns along with any use of [[Admin_Schedule_Aliases|Schedule Aliases]] now support plain language description of the patterns throughout the [[Admin_Web_Application_Guide|UI]], visible while hovering patterns, and in [[REST_Endpoints|REST]] and [[Embedded_API|Embedded]] API responses.&lt;br /&gt;
* All screens supporting UI exports now support JSON ([[Admin_Job_Activity#Exporting_Results|Job Activity]], [[Admin_Jobs#Exporting_Results|Jobs]], [[Admin_Job_Runtime_Preview#Exporting_Results|Runtime Previews]], [[Admin_Job_Chains#Job_Chain_Listing|Job Chains]], [[Admin_Logs#Exporting_Results|Logs]], [[Admin_Notifications#Exporting_Results|Sent Notifications]], [[Admin_User_Management#Exporting_Results|Users]], [[Admin_Custom_Calendars#Calendar_Listing|Calendars]])&lt;br /&gt;
* All screens supporting UI exports and search criteria and/or inline filters now include any specified search criteria and filter text in Excel, XML and JSON downloads ([[Admin_Job_Activity#Exporting_Results|Job Activity]], [[Admin_Jobs#Exporting_Results|Jobs]], [[Admin_Job_Runtime_Preview#Exporting_Results|Runtime Previews]], [[Admin_Job_Chains#Job_Chain_Listing|Job Chains]], [[Admin_Logs#Exporting_Results|Logs]], [[Admin_Notifications#Exporting_Results|Sent Notifications]], [[Admin_User_Management#Exporting_Results|Users]])&lt;br /&gt;
* Support for [[Installation_Guide#Choosing_Email_Support|Jakarta EE mail]] &#039;&#039;&#039;Potential breaking change to automated installer files.&#039;&#039;&#039; &amp;lt;ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using automated installer files from previous releases and had email configured, you will need to add a new section of xml as of Obsidian 5.2.0 to handle a new UserInputPanel. Immediately after the UserInputPanel.16 closing brace, add the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;com.izforge.izpack.panels.UserInputPanel id=&amp;quot;UserInputPanel.17&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;userInput&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;mail.type.selection&amp;quot; value=&amp;quot;javax&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;/userInput&amp;gt;&lt;br /&gt;
&amp;lt;/com.izforge.izpack.panels.UserInputPanel&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/ref&amp;gt;&lt;br /&gt;
* New [[Event_Hooks#REST_Endpoint_Event_Hook|REST Endpoint Event Hook]]&lt;br /&gt;
* [[Key_Server_Proxy|Key Server proxy]] artifact obtained via web download during installation&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Time picker buttons (hour, minute, AM/PM) in [[Admin_Job_Activity#Filtering|Job Activity filtering]] no longer change other elements of the selected time.&lt;br /&gt;
* Cron pattern with [[Cron#Special_Character_Usage|LW]] and any other non-L value in day position no longer also incorrectly evaluates to last day.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.1.1 ==&lt;br /&gt;
Released June 2023&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* [[Authenticator|Native authentication]] no longer fails when user deletes are attempted from the UI.&lt;br /&gt;
* Built in maintenance job [[Built-in_Jobs#Job_History_Cleanup_Job|Job History Cleanup]] no longer leaves deletion candidate CHAIN SKIPPED records in the JOB_HISTORY table in rare circumstances.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.1.0 ==&lt;br /&gt;
Released April 2023&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* [[Admin_Schedule_Aliases#Schedule_Alias_Fragments|Schedule Aliases]] now support fragments for configuration-time substitutions.&lt;br /&gt;
* New convenience job [[Built-in_Jobs#Database_File_Export_Job|Database File Export Job]] for generating basic file extracts from database queries.&lt;br /&gt;
* New convenience job [[Built-in_Jobs#REST_Invocation_Job|REST Invocation Job]] for making simple REST calls and storing results.&lt;br /&gt;
* Performance improvements in job failure handling.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.0.4 == &lt;br /&gt;
Released February 2022&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Log4j2 2.17.1 as fix for [https://logging.apache.org/log4j/2.x/security.html#log4j-2.17.1 RCE vulnerability] where attackers can modify log4j configuration.&lt;br /&gt;
* Restore missing default log4j2 configuration in installation artifacts.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.0.3 == &lt;br /&gt;
Released December 2021&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Log4j2 2.17.0 as fix for [https://logging.apache.org/log4j/2.x/security.html#log4j-2.17.0 DOS vulnerability]&lt;br /&gt;
* Fix native login issue showing as inactive on some databases.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.0.2 == &lt;br /&gt;
Released December 2021&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Log4j2 2.16.0 as permanent fix for [https://logging.apache.org/log4j/2.x/security.html#log4j-2.16.0 RCE vulnerability]&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.0.1 == &lt;br /&gt;
Released December 2021&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Log4j2 2.15.0 as fix for [https://logging.apache.org/log4j/2.x/security.html#log4j-2.15.0 RCE vulnerability]&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
* Fix sporadic native login issue on some databases.&lt;br /&gt;
* Formatting fix in quick installer file&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.0.0 == &lt;br /&gt;
Released August 2021.&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Java 11 (minimum Java version)&lt;br /&gt;
* [[Admin_Schedule_Aliases|Schedule Aliases]] including support in [[REST_Endpoints#Schedule_Alias_Endpoints|REST API]] and [[Embedded_API#ScheduleAliasManager_API|Embedded API]]&lt;br /&gt;
* [[Admin_User_Management#Multi-Factor_Authentication_.28MFA.29|MFA Support]] for UI logins&lt;br /&gt;
* New [[Admin_User_Management#User_Rights|Author and Operator]] roles&lt;br /&gt;
* Convention-based role permissions by [[Admin_User_Management#Job_Folder_Rights|root job folder]] for Write, Author and Operator.&amp;lt;ref&amp;gt;&lt;br /&gt;
There is a possibility of a breaking change to Embedded or REST API use due to the need to change the [[Embedded_API#Enumerations|User Role enumeration]] from a Java enum to an enum-style class to support this feature. Bringing in the upgraded Obsidian library and compiling should reveal any such broken use of these enumerations. Needed changes should be minor and self-explanatory.&amp;lt;/ref&amp;gt;&lt;br /&gt;
* Bundled Jetty 10.0.2&lt;br /&gt;
* Legacy Embedded API (from Obsidian 1.5) dropped&lt;br /&gt;
* Signal handler disabled by default. Enabled only via [[Advanced_Configuration#Miscellaneous_Properties|configuration]].&lt;br /&gt;
* Many [[Advanced_Configuration#Dependent_Libraries|library upgrades]].&lt;br /&gt;
* UI javascript library updates.&lt;br /&gt;
&lt;br /&gt;
== Footnotes ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Main_Page&amp;diff=4175</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Main_Page&amp;diff=4175"/>
		<updated>2026-08-12T21:57:34Z</updated>

		<summary type="html">&lt;p&gt;Craig: Wiki update&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;[[Obsidian]]&#039;&#039;&#039; is a fully-featured Java-based scheduling application which supports load balancing, failover and job workflow. It is designed for [[Deployment Diagram|high availability]], and it is specially built for demanding scheduling environments.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;[[Release_Notes#Obsidian_7.0.2|Version 7.0.2]]&#039;&#039;&#039; is the latest release, and was released in August 2026.&lt;br /&gt;
&lt;br /&gt;
Check out &#039;&#039;&#039;[[Getting Started]]&#039;&#039;&#039; if you are looking to set up Obsidian.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;[[User Guide]]&#039;&#039;&#039; will provide all the information you need to use all of Obsidian&#039;s functionality, and provides a quick view of the features Obsidian supports.&lt;br /&gt;
&lt;br /&gt;
Our &#039;&#039;&#039;[[FAQ]]&#039;&#039;&#039; is also worth checking out if you have questions about Obsidian or want more context about what it does.&lt;br /&gt;
&lt;br /&gt;
== Full Administration Capabilities ==&lt;br /&gt;
&lt;br /&gt;
Obsidian features a rich administration web interface, allowing easy monitoring and management. See the &#039;&#039;&#039;[[Admin Web Application Guide]]&#039;&#039;&#039; for details.&lt;br /&gt;
&lt;br /&gt;
[[Image:ObsidianNav 4.0.png]]&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Installation_Guide&amp;diff=4174</id>
		<title>Installation Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Installation_Guide&amp;diff=4174"/>
		<updated>2026-08-02T02:26:32Z</updated>

		<summary type="html">&lt;p&gt;Craig: /* Offline or restricted-network install */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;This installation guide generally applies to Obsidian versions 4.0 and newer but is specific to 7.0. You may see a few minor differences in 4.x, 5.x and 6.x releases.&lt;br /&gt;
&lt;br /&gt;
This installation guide is a companion to the Obsidian Installer UI. Its purpose is to provide additional detail as to the meaning of various inputs and to provide guidance on advanced usage of the installer.&lt;br /&gt;
&lt;br /&gt;
The installer itself installs and configures the artifacts. The configured artifacts will have their [[Advanced_Configuration#Properties_File|Properties]] file configured according to the choices made during the installation process. You may always change these later or use one of the [[Advanced_Configuration#Properties_File|override]] mechanisms.&lt;br /&gt;
== Running the Obsidian Installer ==&lt;br /&gt;
The installer is an executable JAR file in the Obsidian download zip package available on our [https://web.obsidianscheduler.com/download/ download page].&lt;br /&gt;
&lt;br /&gt;
The installer can be run from the command line as a graphical user interface using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar&amp;lt;/code&amp;gt; or in interactive console mode using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar -console&amp;lt;/code&amp;gt;. Note that you will have to replace the JAR file name with the actual versioned name in your installation.&lt;br /&gt;
&lt;br /&gt;
On some platforms, simple double-clicking the JAR file will start it in graphical interface mode.&lt;br /&gt;
&lt;br /&gt;
If you are doing a version upgrade of Obsidian or are otherwise uninterested in actually configuring the artifacts, you can run the quick start mode to get the default configured artifacts using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar h2-tomcat-quick-start.xml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Obsidian Installer Artifacts ==&lt;br /&gt;
&lt;br /&gt;
The Obsidian Installer installs and configures a number of artifacts. You can choose which artifacts to create, but most users can leave the default options selected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Installation-Packages.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Obsidian WAR&#039;&#039;&#039; - This is the Obsidian Web Admin UI with scheduler component enabled. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/obsidian.war!/WEB-INF/classes/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Standalone Admin WAR&#039;&#039;&#039; - This is the Obsidian Web Admin UI with scheduler component disabled. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/standaloneObsidianAdmin.war!/WEB-INF/classes/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Standalone Obsidian Runtime&#039;&#039;&#039; - This is a runtime folder containing the libraries and configuration necessary for running the Obsidian Scheduler component either as a standalone module or for use as an Embedded Scheduler in your application. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/standalone/obsidian-props.jar!/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian License Proxy&#039;&#039;&#039; - Allows for local license leasing from a [[Key_Server_Proxy|Key Server Proxy]] that ultimately leases licenses from the Obsidian License server.&lt;br /&gt;
# &#039;&#039;&#039;Documentation&#039;&#039;&#039; - README and Embedded API Javadoc.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; — An embedded Apache Tomcat runtime for running Obsidian locally without an external servlet container. Requires the Obsidian WAR pack.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; — An embedded Apache Tomcat runtime for running the standalone admin WAR locally. Requires the Obsidian Standalone Admin WAR pack.&lt;br /&gt;
# &#039;&#039;&#039;License&#039;&#039;&#039; - Obsidian and 3rd party license information.&lt;br /&gt;
&lt;br /&gt;
==== Deployment options ====&lt;br /&gt;
&lt;br /&gt;
Obsidian 7 offers four deployment profiles in the installer:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Obsidian WAR&#039;&#039;&#039; — deploy &#039;&#039;&#039;obsidian.war&#039;&#039;&#039; to your own servlet container (Tomcat, WebLogic, etc.).&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; — adds an embedded Tomcat runtime and &#039;&#039;&#039;webObsidian&#039;&#039;&#039; scripts to run &#039;&#039;&#039;obsidian.war&#039;&#039;&#039; locally (&#039;&#039;&#039;start scheduler&#039;&#039;&#039;). &#039;&#039;&#039;Requires&#039;&#039;&#039; the Obsidian WAR pack.&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Standalone Admin WAR&#039;&#039;&#039; — admin console WAR for an external servlet container.&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; — embedded runtime for the admin WAR (&#039;&#039;&#039;start adminOnly&#039;&#039;&#039;). &#039;&#039;&#039;Requires&#039;&#039;&#039; the Standalone Admin WAR pack.&lt;br /&gt;
&lt;br /&gt;
Embedded Tomcat does &#039;&#039;&#039;not&#039;&#039;&#039; replace the WAR on disk — it runs the installed WAR via the helper. Do not select an embed pack without its matching WAR pack. For how to start, stop and change the ports of the embedded runtime, see [[Getting_Started#Starting_Obsidian_with_embedded_Tomcat|Starting Obsidian with embedded Tomcat]].&lt;br /&gt;
&lt;br /&gt;
==== Linux desktop and systemd integration ====&lt;br /&gt;
&lt;br /&gt;
On a Unix host, an embedded Tomcat pack also copies a &#039;&#039;&#039;linux/&#039;&#039;&#039; directory under your install path holding user-level systemd and desktop-entry templates, and the installer offers to activate them. Nothing is installed system-wide and no root privileges are required.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Embedded Tomcat - Linux integration (optional)&#039;&#039;&#039; panel carries the checkbox &#039;&#039;&#039;Install Linux desktop and user systemd integration&#039;&#039;&#039; (unchecked by default). Tick it to have the installer run the integration script for you; the option only takes effect if you also selected an embedded Tomcat pack. Leaving it unchecked still copies the templates, so you can opt in later by running this from the install directory:&lt;br /&gt;
&lt;br /&gt;
 ./linux/install-embedded-linux-integration.sh &amp;quot;$PWD&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Either route writes two files, for the installing user only:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/.config/systemd/user/obsidian-embedded@scheduler.service&#039;&#039;&lt;br /&gt;
* &#039;&#039;~/.local/share/applications/obsidian-embedded.desktop&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;XDG_CONFIG_HOME&#039;&#039; and &#039;&#039;XDG_DATA_HOME&#039;&#039; are honoured when set.)&lt;br /&gt;
&lt;br /&gt;
For an automated (headless) install, request the integration with:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;entry key=&amp;quot;embed.linux.integration&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This panel is &#039;&#039;&#039;not shown on Windows installs&#039;&#039;&#039; — the systemd and desktop templates are Linux-only. On Windows, run &#039;&#039;&#039;webObsidian.bat&#039;&#039;&#039; manually or wrap it with your own service tooling.&lt;br /&gt;
&lt;br /&gt;
Installing these files does not enable or start anything. To enable, stop, reconfigure or remove the service, see [[Getting_Started#Running_Obsidian_as_a_Linux_user_service|Running Obsidian as a Linux user service]].&lt;br /&gt;
&lt;br /&gt;
== Obsidian Configuration ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Obsidian.png]]&lt;br /&gt;
&lt;br /&gt;
First, you must choose what [[Authenticator]] mechanism will be used within Obsidian. Obsidian supports five authentication methods: native (database), LDAP, OAuth (OIDC), SAML 2.0, and custom. Select &#039;&#039;Native (Database)&#039;&#039;, &#039;&#039;LDAP&#039;&#039;, &#039;&#039;OAuth (OIDC)&#039;&#039;, &#039;&#039;SAML 2.0&#039;&#039;, or &#039;&#039;Other&#039;&#039; (custom). &#039;&#039;Native (Database)&#039;&#039; requires no additional configuration and is what most users will select at least to start.&lt;br /&gt;
&lt;br /&gt;
Next, you&#039;ll want to select the email usage type. We highly recommend you configure Obsidian for email use as it will allow you to benefit from the event [[Event_Notifications|notification]] and [[Admin_Notifications|subscription]] support in Obsidian.&lt;br /&gt;
&lt;br /&gt;
Then you&#039;ll configure the log file location, license key (optional) and registered company name (optional). Registered company name is only required for our Site License users.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== OAuth / OIDC Configuration ====&lt;br /&gt;
&lt;br /&gt;
If you selected &#039;&#039;&#039;OAuth (OIDC)&#039;&#039;&#039; as the authentication type, the installer displays additional panels for OAuth configuration.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Provider and Common Settings =====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-OAuth_OIDC.png]]&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Identity Provider&#039;&#039;&#039; — Select your provider: Keycloak, Microsoft Entra ID, Okta, Auth0, or Generic OIDC.&lt;br /&gt;
* &#039;&#039;&#039;SSO Button label&#039;&#039;&#039; — Label shown on the &amp;quot;Sign in with…&amp;quot; button (e.g. &#039;&#039;Keycloak&#039;&#039;, &#039;&#039;Acme Corp SSO&#039;&#039;). If left blank the button reads &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
* &#039;&#039;&#039;Redirect URI&#039;&#039;&#039; — The callback URL Obsidian receives after IdP login. Must be registered at the IdP (e.g. &#039;&#039;https://app.example.com/oauth/callback&#039;&#039;).&lt;br /&gt;
* &#039;&#039;&#039;Logout mode&#039;&#039;&#039; — &#039;&#039;local&#039;&#039; (default) or &#039;&#039;rp&#039;&#039; (RP-initiated logout via the IdP&#039;s end_session_endpoint).&lt;br /&gt;
&lt;br /&gt;
===== Provider-Specific Settings =====&lt;br /&gt;
&lt;br /&gt;
Fill in the connection details for your chosen provider.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Keycloak.png]]&lt;br /&gt;
&lt;br /&gt;
;Keycloak&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — e.g. &#039;&#039;https://keycloak.example.com/realms/myrealm&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from the Keycloak client settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — recommended: &#039;&#039;openid profile email groups&#039;&#039;. &lt;br /&gt;
Note: Keycloak does not include a &#039;&#039;groups&#039;&#039; claim by default — a Group Membership mapper must be configured in the client&#039;s Mappers tab.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Microsoft-Entra-ID.png]]&lt;br /&gt;
&lt;br /&gt;
;Microsoft Entra ID&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — &#039;&#039;https://login.microsoftonline.com/&amp;lt;tenant-id&amp;gt;/v2.0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; — the Application (client) ID from the Azure portal.&lt;br /&gt;
* &#039;&#039;&#039;Client Secret&#039;&#039;&#039; — a client secret value from the Azure portal.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email&#039;&#039;. &lt;br /&gt;
Note: the &#039;&#039;groups&#039;&#039; claim contains GUIDs by default; configure Optional Claims in the Azure portal to receive group display names. &lt;br /&gt;
&#039;&#039;&#039;Multi-tenant applications are not supported in Obsidian 7.0.x.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Okta.png]]&lt;br /&gt;
&lt;br /&gt;
;Okta&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — use the &#039;&#039;&#039;Custom Authorization Server&#039;&#039;&#039; URL, e.g. &#039;&#039;https://&amp;lt;domain&amp;gt;.okta.com/oauth2/default&#039;&#039;. Do &#039;&#039;&#039;not&#039;&#039;&#039; use the Org Authorization Server URL.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from Okta application settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email groups&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Auth0.png]]&lt;br /&gt;
&lt;br /&gt;
;Auth0&lt;br /&gt;
* &#039;&#039;&#039;Domain / Issuer URL&#039;&#039;&#039; — e.g. &#039;&#039;https://&amp;lt;domain&amp;gt;.auth0.com/&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from Auth0 application settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email&#039;&#039;.&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; Auth0 issues opaque access tokens by default which are &#039;&#039;&#039;not supported&#039;&#039;&#039; on Obsidian&#039;s REST/Bearer path — configure a custom API in Auth0 to get JWT access tokens.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Generic_OIDC.png]]&lt;br /&gt;
&lt;br /&gt;
;Generic OIDC&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — the base URL; Obsidian appends &#039;&#039;/.well-known/openid-configuration&#039;&#039; to discover endpoints.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from the provider.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — at minimum &#039;&#039;openid&#039;&#039;; add &#039;&#039;profile&#039;&#039; and your groups scope as needed.&lt;br /&gt;
&lt;br /&gt;
===== Group → Role Mapping =====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-OAuth-Group-Role-Mapping.png]]&lt;br /&gt;
&lt;br /&gt;
The final OAuth panel maps IdP group values to Obsidian roles. &#039;&#039;&#039;Require role assignment&#039;&#039;&#039; (default yes) — when enabled, users with no matching roles cannot complete browser login. At least one mapping to the &#039;&#039;&#039;Admin&#039;&#039;&#039; role is required. The installer supports up to 10 group→role pairs; additional pairs can be added directly to the configuration file after install. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties]] for the full property reference.&lt;br /&gt;
&lt;br /&gt;
===== Registering the redirect URI at the IdP =====&lt;br /&gt;
&lt;br /&gt;
Before starting Obsidian, register the redirect URI shown in the installer at your IdP: Keycloak — Client → Settings → Valid Redirect URIs; Entra ID — App Registration → Authentication → Redirect URIs; Okta — Application → General Settings → Sign-in Redirect URIs; Auth0 — Application → Settings → Allowed Callback URLs. The URI must match &#039;&#039;com.carfey.suite.security.OAuthAuthenticator.redirectUri&#039;&#039; exactly, including scheme and port.&lt;br /&gt;
&lt;br /&gt;
==== SAML 2.0 Configuration ====&lt;br /&gt;
&lt;br /&gt;
If you selected &#039;&#039;&#039;SAML 2.0&#039;&#039;&#039; as the authentication type, the installer displays additional panels that configure the Service Provider identity, SAML signing credentials, federation metadata, attribute options, and group→role mapping.&lt;br /&gt;
&lt;br /&gt;
===== Service Provider =====&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SP Entity ID&#039;&#039;&#039; — HTTPS URI registered as the SAML SP entity (must match IdP registrations).&lt;br /&gt;
* &#039;&#039;&#039;Assertion Consumer Service URL&#039;&#039;&#039; — HTTPS ACS endpoint where SAML responses arrive (HTTP-POST); aligns with the ACS in &#039;&#039;/saml/metadata&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;SP Single Logout Service URL&#039;&#039;&#039; — Optional HTTPS URL for IdP-initiated SLO when logout mode targets IdP-mediated logout.&lt;br /&gt;
* &#039;&#039;&#039;SSO button label&#039;&#039;&#039; — Required label shown on &#039;&#039;&#039;Sign in with …&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Logout mode&#039;&#039;&#039; — &#039;&#039;&#039;Local&#039;&#039;&#039; (Obsidian-only) or &#039;&#039;&#039;IdP / SLO&#039;&#039;&#039; (participates in SAML single logout when advertised).&lt;br /&gt;
* &#039;&#039;&#039;Post-logout redirect URI&#039;&#039;&#039; — Optional HTTPS landing URI after &#039;&#039;LogoutResponse&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Keystore path / alias / passwords&#039;&#039;&#039; — Required when signing SAML requests (password field is dual-entry validated).&lt;br /&gt;
* &#039;&#039;&#039;Sign SAML authentication requests&#039;&#039;&#039; — Toggle (installer default &#039;&#039;&#039;true&#039;&#039;&#039;).&lt;br /&gt;
* &#039;&#039;&#039;Require signed SAML assertions&#039;&#039;&#039; — Toggle (recommended &#039;&#039;&#039;true&#039;&#039;&#039; for production).&lt;br /&gt;
&lt;br /&gt;
===== Identity Provider =====&lt;br /&gt;
&lt;br /&gt;
Choose the IdP type: Microsoft Entra ID (Azure AD), AD FS (on-premises), Okta, Auth0, or Generic SAML 2.0 (covers Keycloak &#039;&#039;/protocol/saml/descriptor&#039;&#039;, Ping, Google Workspace, and similar). Follow-on screens seed example metadata URLs and default attribute names.&lt;br /&gt;
&lt;br /&gt;
Provide &#039;&#039;&#039;either&#039;&#039;&#039; an HTTPS federation metadata URL &#039;&#039;&#039;or&#039;&#039;&#039; a local federation XML absolute path — choose exactly one. Later panels cover attribute options (&#039;&#039;friendlyName&#039;&#039;, &#039;&#039;valuePath&#039;&#039;, &#039;&#039;stripPrefix&#039;&#039;, &#039;&#039;attribute.user&#039;&#039;, &#039;&#039;attribute.displayName&#039;&#039;, &#039;&#039;ForceAuthn&#039;&#039;, &#039;&#039;RequestedAuthnContext&#039;&#039;, NameID format).&lt;br /&gt;
&lt;br /&gt;
===== Group → Role Mapping =====&lt;br /&gt;
&lt;br /&gt;
The closing panels map IdP group attribute values to Obsidian roles, including &#039;&#039;&#039;Require role assignment&#039;&#039;&#039; (default yes) and optional &#039;&#039;dynamicRole.prefix&#039;&#039; / &#039;&#039;suffix&#039;&#039;. At least one mapping to the &#039;&#039;&#039;Admin&#039;&#039;&#039; role is required. See [[Advanced_Configuration#SAML_Authentication_Properties|SAML Authentication Properties]] for the full property reference.&lt;br /&gt;
&lt;br /&gt;
===== SP metadata artefacts =====&lt;br /&gt;
&lt;br /&gt;
During install the tooling writes &#039;&#039;&#039;sp-metadata.xml&#039;&#039;&#039; under the Obsidian deployment directory; the same metadata is available live at &#039;&#039;&#039;/saml/metadata&#039;&#039;&#039; after startup. Register whichever artefact your IdP admin prefers.&lt;br /&gt;
&lt;br /&gt;
===== Before you complete install =====&lt;br /&gt;
&lt;br /&gt;
* Ensure ACS and Entity ID &#039;&#039;&#039;https&#039;&#039;&#039; URLs are reachable from IdPs (align with reverse proxies).&lt;br /&gt;
* For Entra &#039;&#039;&#039;150+&#039;&#039;&#039; group memberships in SAML assertions, expect attribute suppression — filter groups or map assigned app roles instead. (OIDC uses a &#039;&#039;&#039;200&#039;&#039;&#039; group threshold; the numbers differ by protocol.)&lt;br /&gt;
&lt;br /&gt;
==== LDAP Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.LDAP.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve selected LDAP Authentication, this screen takes you through configuring the server address and the various elements used to grant access. You should familiarize yourself with Obsidian [[Authenticator#Roles|Roles]]. The &#039;&#039;Access DN&#039;&#039; configuration element grants Read access to Obsidian application. You may use the same DN for more than one Role should you so wish.&lt;br /&gt;
&lt;br /&gt;
==== Custom Authenticator Configuration====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.CustomAuthenticator.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have chosen to use your own authentication mechanism, you must enter the fully qualified classname here.&lt;br /&gt;
&lt;br /&gt;
==== Database Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Database.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Next comes database configuration. If you are using JNDI, leave the username and password fields blank. If using JDBC URL, username and password fields are required.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;Database connections per instance&#039;&#039; and &#039;&#039;Database connection timeout(millis)&#039;&#039; fields are required and provide directive to the connection pool.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Database table name prefix&#039;&#039; is optional and is typically used when Obsidian will be colocated in an existing database/schema.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Database schema (Oracle/PostgreSQL)&#039;&#039; - This is used to allow for an alternate schema other than the default user&#039;s schema. We also recommend setting this value with Oracle/PostgreSQL when using JNDI as it allows more efficient database metadata loading.&lt;br /&gt;
&lt;br /&gt;
By default, the Obsidian installation will only include the JDBC libraries necessary for your particular database. At times you may wish to include the others to be able to change between databases. In those cases, check &#039;&#039;Include all supported JDBC libs&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Email Configuration ====&lt;br /&gt;
&lt;br /&gt;
If not using JNDI for mail sessions, provide the server and authentication details as required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.SSL.Email.png]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.TLS.Email.png]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.Open.Email.png]]&lt;br /&gt;
&lt;br /&gt;
==== JNDI Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.JNDI.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have selected JNDI for mail sessions, provide the JNDI path here. If you are using JNDI for Database connections, specify the database type.&lt;br /&gt;
&lt;br /&gt;
==== Configuring 3rd Party Library Conflict Management ====&lt;br /&gt;
[[Image:JarJar-Obsidian-6.0.0.png]]&lt;br /&gt;
&lt;br /&gt;
The Obsidian installer allows you to use [https://code.google.com/archive/p/jarjar JarJar] to handle potential conflicts between Obsidian&#039;s use of 3rd party libraries and versions used within your application. On this screen, simply select which libraries to which you wish to apply the JarJar bytecode modification process. This is optional and is skipped if no libraries are selected. Any jars not listed for which you are using more recent versions can be used and are therefore not included as options in the JarJar processing.&lt;br /&gt;
&lt;br /&gt;
==== Selecting Script Libraries ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-6.0.0-Choose-Scripting-Libraries.png]]&lt;br /&gt;
&lt;br /&gt;
The Obsidian installer allows you to select which script libraries you wish to include. Jython and JRuby are incompatible with each other, so the installer will only allow one of the two selected. If all script libraries are deselected, the only script jobs that can be run in Obsidian will be Javascript jobs as that engine is built into Java.&lt;br /&gt;
&lt;br /&gt;
==== Choosing Email Support ====&lt;br /&gt;
As of Obsidian 5.2.0, you can choose between JavaMail (javax) and Jakarta mail (Jakarta EE) implementations. When choosing Jakarta, you can also choose to bundle the Angus Jakarta compatible implementation. &lt;br /&gt;
&lt;br /&gt;
If you are using JNDI, this will only include the relevant support in the Obsidian WAR artifacts and the actual libraries in use must be provided by you and bundled with your container. If the standalone scheduler is selected along with JNDI, JNDI is assumed only relevant for WARs. As such, default libraries for either JavaMail or Jakarta will be included in the standalone installation.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.EmailImplementation.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
==== Additional configuration items ====&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 5.5.0, if you require any additional configuration items such as additional appenders/loggers or event hook configurations, you can add them here.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.5.0.Extra.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
==== Offline or restricted-network install ====&lt;br /&gt;
&lt;br /&gt;
Obsidian 7.x.x resolves third-party libraries when you run the installer. For environments without direct access to Maven Central:&lt;br /&gt;
&lt;br /&gt;
# Pre-stage a local dependency cache containing every coordinate listed in the Obsidian dependency inventory for your release (contact Carfey support or your account team for the checklist file matching &#039;&#039;&#039;Obsidian-Install-7.x.x.jar&#039;&#039;&#039;).&lt;br /&gt;
# Copy the cache to the install host. Two layouts are supported:&lt;br /&gt;
#* &#039;&#039;&#039;Flat:&#039;&#039;&#039; one file per installed JAR name directly under the cache root.&lt;br /&gt;
#* &#039;&#039;&#039;File Maven repo:&#039;&#039;&#039; &#039;&#039;&#039;&amp;lt;group&amp;gt;/&amp;lt;artifact&amp;gt;/&amp;lt;version&amp;gt;/&amp;lt;installed-filename&amp;gt;.jar&#039;&#039;&#039;&lt;br /&gt;
# Run the installer with offline mode and the cache directory:&lt;br /&gt;
&lt;br /&gt;
 java -Dobsidian.install.deps.offline=true -Dobsidian.install.deps.cache.dir=/path/to/cache -jar Obsidian-Install-7.x.x.jar&lt;br /&gt;
&lt;br /&gt;
If a required library is missing from the cache, the install stops with an error naming the coordinate — add that artifact to the cache and re-run.&lt;br /&gt;
&lt;br /&gt;
===== Corporate Maven mirror (online) =====&lt;br /&gt;
&lt;br /&gt;
When your site mirrors Maven Central, point the installer at your repository base URL:&lt;br /&gt;
&lt;br /&gt;
 java -Dobsidian.install.deps.repo.url=https://nexus.example.com/repository/maven-public/ -jar Obsidian-Install-7.x.x.jar&lt;br /&gt;
&lt;br /&gt;
The installer reuses resolved artifacts for the duration of one install session.&lt;br /&gt;
&lt;br /&gt;
== Completing the Installation ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Finished.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Once you&#039;ve completed the Installation and Configuration screens, fully configured Obsidian Scheduler artifacts are now ready for you to use in the installation path you selected.&lt;br /&gt;
&lt;br /&gt;
Should you wish to automate future installations with the same configuration, click &#039;&#039;Generate an automatic installation script&#039;&#039;. This will prompt you to save an XML file that can be used for future installations using the automated install procedure &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar my-obsidian-configuration.xml&amp;lt;/code&amp;gt;. Every effort is made to ensure compatibility of these automated install files between versions. Any incompatibility will be noted in the [[Release_Notes|Release Notes]]. These automated installer files can also be used as templates for other environments, modifying them as necessary.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Installation_Guide&amp;diff=4173</id>
		<title>Installation Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Installation_Guide&amp;diff=4173"/>
		<updated>2026-08-02T02:26:07Z</updated>

		<summary type="html">&lt;p&gt;Craig: /* Offline or restricted-network install */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;This installation guide generally applies to Obsidian versions 4.0 and newer but is specific to 7.0. You may see a few minor differences in 4.x, 5.x and 6.x releases.&lt;br /&gt;
&lt;br /&gt;
This installation guide is a companion to the Obsidian Installer UI. Its purpose is to provide additional detail as to the meaning of various inputs and to provide guidance on advanced usage of the installer.&lt;br /&gt;
&lt;br /&gt;
The installer itself installs and configures the artifacts. The configured artifacts will have their [[Advanced_Configuration#Properties_File|Properties]] file configured according to the choices made during the installation process. You may always change these later or use one of the [[Advanced_Configuration#Properties_File|override]] mechanisms.&lt;br /&gt;
== Running the Obsidian Installer ==&lt;br /&gt;
The installer is an executable JAR file in the Obsidian download zip package available on our [https://web.obsidianscheduler.com/download/ download page].&lt;br /&gt;
&lt;br /&gt;
The installer can be run from the command line as a graphical user interface using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar&amp;lt;/code&amp;gt; or in interactive console mode using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar -console&amp;lt;/code&amp;gt;. Note that you will have to replace the JAR file name with the actual versioned name in your installation.&lt;br /&gt;
&lt;br /&gt;
On some platforms, simple double-clicking the JAR file will start it in graphical interface mode.&lt;br /&gt;
&lt;br /&gt;
If you are doing a version upgrade of Obsidian or are otherwise uninterested in actually configuring the artifacts, you can run the quick start mode to get the default configured artifacts using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar h2-tomcat-quick-start.xml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Obsidian Installer Artifacts ==&lt;br /&gt;
&lt;br /&gt;
The Obsidian Installer installs and configures a number of artifacts. You can choose which artifacts to create, but most users can leave the default options selected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Installation-Packages.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Obsidian WAR&#039;&#039;&#039; - This is the Obsidian Web Admin UI with scheduler component enabled. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/obsidian.war!/WEB-INF/classes/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Standalone Admin WAR&#039;&#039;&#039; - This is the Obsidian Web Admin UI with scheduler component disabled. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/standaloneObsidianAdmin.war!/WEB-INF/classes/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Standalone Obsidian Runtime&#039;&#039;&#039; - This is a runtime folder containing the libraries and configuration necessary for running the Obsidian Scheduler component either as a standalone module or for use as an Embedded Scheduler in your application. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/standalone/obsidian-props.jar!/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian License Proxy&#039;&#039;&#039; - Allows for local license leasing from a [[Key_Server_Proxy|Key Server Proxy]] that ultimately leases licenses from the Obsidian License server.&lt;br /&gt;
# &#039;&#039;&#039;Documentation&#039;&#039;&#039; - README and Embedded API Javadoc.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; — An embedded Apache Tomcat runtime for running Obsidian locally without an external servlet container. Requires the Obsidian WAR pack.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; — An embedded Apache Tomcat runtime for running the standalone admin WAR locally. Requires the Obsidian Standalone Admin WAR pack.&lt;br /&gt;
# &#039;&#039;&#039;License&#039;&#039;&#039; - Obsidian and 3rd party license information.&lt;br /&gt;
&lt;br /&gt;
==== Deployment options ====&lt;br /&gt;
&lt;br /&gt;
Obsidian 7 offers four deployment profiles in the installer:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Obsidian WAR&#039;&#039;&#039; — deploy &#039;&#039;&#039;obsidian.war&#039;&#039;&#039; to your own servlet container (Tomcat, WebLogic, etc.).&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; — adds an embedded Tomcat runtime and &#039;&#039;&#039;webObsidian&#039;&#039;&#039; scripts to run &#039;&#039;&#039;obsidian.war&#039;&#039;&#039; locally (&#039;&#039;&#039;start scheduler&#039;&#039;&#039;). &#039;&#039;&#039;Requires&#039;&#039;&#039; the Obsidian WAR pack.&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Standalone Admin WAR&#039;&#039;&#039; — admin console WAR for an external servlet container.&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; — embedded runtime for the admin WAR (&#039;&#039;&#039;start adminOnly&#039;&#039;&#039;). &#039;&#039;&#039;Requires&#039;&#039;&#039; the Standalone Admin WAR pack.&lt;br /&gt;
&lt;br /&gt;
Embedded Tomcat does &#039;&#039;&#039;not&#039;&#039;&#039; replace the WAR on disk — it runs the installed WAR via the helper. Do not select an embed pack without its matching WAR pack. For how to start, stop and change the ports of the embedded runtime, see [[Getting_Started#Starting_Obsidian_with_embedded_Tomcat|Starting Obsidian with embedded Tomcat]].&lt;br /&gt;
&lt;br /&gt;
==== Linux desktop and systemd integration ====&lt;br /&gt;
&lt;br /&gt;
On a Unix host, an embedded Tomcat pack also copies a &#039;&#039;&#039;linux/&#039;&#039;&#039; directory under your install path holding user-level systemd and desktop-entry templates, and the installer offers to activate them. Nothing is installed system-wide and no root privileges are required.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Embedded Tomcat - Linux integration (optional)&#039;&#039;&#039; panel carries the checkbox &#039;&#039;&#039;Install Linux desktop and user systemd integration&#039;&#039;&#039; (unchecked by default). Tick it to have the installer run the integration script for you; the option only takes effect if you also selected an embedded Tomcat pack. Leaving it unchecked still copies the templates, so you can opt in later by running this from the install directory:&lt;br /&gt;
&lt;br /&gt;
 ./linux/install-embedded-linux-integration.sh &amp;quot;$PWD&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Either route writes two files, for the installing user only:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/.config/systemd/user/obsidian-embedded@scheduler.service&#039;&#039;&lt;br /&gt;
* &#039;&#039;~/.local/share/applications/obsidian-embedded.desktop&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;XDG_CONFIG_HOME&#039;&#039; and &#039;&#039;XDG_DATA_HOME&#039;&#039; are honoured when set.)&lt;br /&gt;
&lt;br /&gt;
For an automated (headless) install, request the integration with:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;entry key=&amp;quot;embed.linux.integration&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This panel is &#039;&#039;&#039;not shown on Windows installs&#039;&#039;&#039; — the systemd and desktop templates are Linux-only. On Windows, run &#039;&#039;&#039;webObsidian.bat&#039;&#039;&#039; manually or wrap it with your own service tooling.&lt;br /&gt;
&lt;br /&gt;
Installing these files does not enable or start anything. To enable, stop, reconfigure or remove the service, see [[Getting_Started#Running_Obsidian_as_a_Linux_user_service|Running Obsidian as a Linux user service]].&lt;br /&gt;
&lt;br /&gt;
== Obsidian Configuration ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Obsidian.png]]&lt;br /&gt;
&lt;br /&gt;
First, you must choose what [[Authenticator]] mechanism will be used within Obsidian. Obsidian supports five authentication methods: native (database), LDAP, OAuth (OIDC), SAML 2.0, and custom. Select &#039;&#039;Native (Database)&#039;&#039;, &#039;&#039;LDAP&#039;&#039;, &#039;&#039;OAuth (OIDC)&#039;&#039;, &#039;&#039;SAML 2.0&#039;&#039;, or &#039;&#039;Other&#039;&#039; (custom). &#039;&#039;Native (Database)&#039;&#039; requires no additional configuration and is what most users will select at least to start.&lt;br /&gt;
&lt;br /&gt;
Next, you&#039;ll want to select the email usage type. We highly recommend you configure Obsidian for email use as it will allow you to benefit from the event [[Event_Notifications|notification]] and [[Admin_Notifications|subscription]] support in Obsidian.&lt;br /&gt;
&lt;br /&gt;
Then you&#039;ll configure the log file location, license key (optional) and registered company name (optional). Registered company name is only required for our Site License users.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== OAuth / OIDC Configuration ====&lt;br /&gt;
&lt;br /&gt;
If you selected &#039;&#039;&#039;OAuth (OIDC)&#039;&#039;&#039; as the authentication type, the installer displays additional panels for OAuth configuration.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Provider and Common Settings =====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-OAuth_OIDC.png]]&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Identity Provider&#039;&#039;&#039; — Select your provider: Keycloak, Microsoft Entra ID, Okta, Auth0, or Generic OIDC.&lt;br /&gt;
* &#039;&#039;&#039;SSO Button label&#039;&#039;&#039; — Label shown on the &amp;quot;Sign in with…&amp;quot; button (e.g. &#039;&#039;Keycloak&#039;&#039;, &#039;&#039;Acme Corp SSO&#039;&#039;). If left blank the button reads &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
* &#039;&#039;&#039;Redirect URI&#039;&#039;&#039; — The callback URL Obsidian receives after IdP login. Must be registered at the IdP (e.g. &#039;&#039;https://app.example.com/oauth/callback&#039;&#039;).&lt;br /&gt;
* &#039;&#039;&#039;Logout mode&#039;&#039;&#039; — &#039;&#039;local&#039;&#039; (default) or &#039;&#039;rp&#039;&#039; (RP-initiated logout via the IdP&#039;s end_session_endpoint).&lt;br /&gt;
&lt;br /&gt;
===== Provider-Specific Settings =====&lt;br /&gt;
&lt;br /&gt;
Fill in the connection details for your chosen provider.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Keycloak.png]]&lt;br /&gt;
&lt;br /&gt;
;Keycloak&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — e.g. &#039;&#039;https://keycloak.example.com/realms/myrealm&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from the Keycloak client settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — recommended: &#039;&#039;openid profile email groups&#039;&#039;. &lt;br /&gt;
Note: Keycloak does not include a &#039;&#039;groups&#039;&#039; claim by default — a Group Membership mapper must be configured in the client&#039;s Mappers tab.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Microsoft-Entra-ID.png]]&lt;br /&gt;
&lt;br /&gt;
;Microsoft Entra ID&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — &#039;&#039;https://login.microsoftonline.com/&amp;lt;tenant-id&amp;gt;/v2.0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; — the Application (client) ID from the Azure portal.&lt;br /&gt;
* &#039;&#039;&#039;Client Secret&#039;&#039;&#039; — a client secret value from the Azure portal.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email&#039;&#039;. &lt;br /&gt;
Note: the &#039;&#039;groups&#039;&#039; claim contains GUIDs by default; configure Optional Claims in the Azure portal to receive group display names. &lt;br /&gt;
&#039;&#039;&#039;Multi-tenant applications are not supported in Obsidian 7.0.x.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Okta.png]]&lt;br /&gt;
&lt;br /&gt;
;Okta&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — use the &#039;&#039;&#039;Custom Authorization Server&#039;&#039;&#039; URL, e.g. &#039;&#039;https://&amp;lt;domain&amp;gt;.okta.com/oauth2/default&#039;&#039;. Do &#039;&#039;&#039;not&#039;&#039;&#039; use the Org Authorization Server URL.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from Okta application settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email groups&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Auth0.png]]&lt;br /&gt;
&lt;br /&gt;
;Auth0&lt;br /&gt;
* &#039;&#039;&#039;Domain / Issuer URL&#039;&#039;&#039; — e.g. &#039;&#039;https://&amp;lt;domain&amp;gt;.auth0.com/&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from Auth0 application settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email&#039;&#039;.&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; Auth0 issues opaque access tokens by default which are &#039;&#039;&#039;not supported&#039;&#039;&#039; on Obsidian&#039;s REST/Bearer path — configure a custom API in Auth0 to get JWT access tokens.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Generic_OIDC.png]]&lt;br /&gt;
&lt;br /&gt;
;Generic OIDC&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — the base URL; Obsidian appends &#039;&#039;/.well-known/openid-configuration&#039;&#039; to discover endpoints.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from the provider.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — at minimum &#039;&#039;openid&#039;&#039;; add &#039;&#039;profile&#039;&#039; and your groups scope as needed.&lt;br /&gt;
&lt;br /&gt;
===== Group → Role Mapping =====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-OAuth-Group-Role-Mapping.png]]&lt;br /&gt;
&lt;br /&gt;
The final OAuth panel maps IdP group values to Obsidian roles. &#039;&#039;&#039;Require role assignment&#039;&#039;&#039; (default yes) — when enabled, users with no matching roles cannot complete browser login. At least one mapping to the &#039;&#039;&#039;Admin&#039;&#039;&#039; role is required. The installer supports up to 10 group→role pairs; additional pairs can be added directly to the configuration file after install. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties]] for the full property reference.&lt;br /&gt;
&lt;br /&gt;
===== Registering the redirect URI at the IdP =====&lt;br /&gt;
&lt;br /&gt;
Before starting Obsidian, register the redirect URI shown in the installer at your IdP: Keycloak — Client → Settings → Valid Redirect URIs; Entra ID — App Registration → Authentication → Redirect URIs; Okta — Application → General Settings → Sign-in Redirect URIs; Auth0 — Application → Settings → Allowed Callback URLs. The URI must match &#039;&#039;com.carfey.suite.security.OAuthAuthenticator.redirectUri&#039;&#039; exactly, including scheme and port.&lt;br /&gt;
&lt;br /&gt;
==== SAML 2.0 Configuration ====&lt;br /&gt;
&lt;br /&gt;
If you selected &#039;&#039;&#039;SAML 2.0&#039;&#039;&#039; as the authentication type, the installer displays additional panels that configure the Service Provider identity, SAML signing credentials, federation metadata, attribute options, and group→role mapping.&lt;br /&gt;
&lt;br /&gt;
===== Service Provider =====&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SP Entity ID&#039;&#039;&#039; — HTTPS URI registered as the SAML SP entity (must match IdP registrations).&lt;br /&gt;
* &#039;&#039;&#039;Assertion Consumer Service URL&#039;&#039;&#039; — HTTPS ACS endpoint where SAML responses arrive (HTTP-POST); aligns with the ACS in &#039;&#039;/saml/metadata&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;SP Single Logout Service URL&#039;&#039;&#039; — Optional HTTPS URL for IdP-initiated SLO when logout mode targets IdP-mediated logout.&lt;br /&gt;
* &#039;&#039;&#039;SSO button label&#039;&#039;&#039; — Required label shown on &#039;&#039;&#039;Sign in with …&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Logout mode&#039;&#039;&#039; — &#039;&#039;&#039;Local&#039;&#039;&#039; (Obsidian-only) or &#039;&#039;&#039;IdP / SLO&#039;&#039;&#039; (participates in SAML single logout when advertised).&lt;br /&gt;
* &#039;&#039;&#039;Post-logout redirect URI&#039;&#039;&#039; — Optional HTTPS landing URI after &#039;&#039;LogoutResponse&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Keystore path / alias / passwords&#039;&#039;&#039; — Required when signing SAML requests (password field is dual-entry validated).&lt;br /&gt;
* &#039;&#039;&#039;Sign SAML authentication requests&#039;&#039;&#039; — Toggle (installer default &#039;&#039;&#039;true&#039;&#039;&#039;).&lt;br /&gt;
* &#039;&#039;&#039;Require signed SAML assertions&#039;&#039;&#039; — Toggle (recommended &#039;&#039;&#039;true&#039;&#039;&#039; for production).&lt;br /&gt;
&lt;br /&gt;
===== Identity Provider =====&lt;br /&gt;
&lt;br /&gt;
Choose the IdP type: Microsoft Entra ID (Azure AD), AD FS (on-premises), Okta, Auth0, or Generic SAML 2.0 (covers Keycloak &#039;&#039;/protocol/saml/descriptor&#039;&#039;, Ping, Google Workspace, and similar). Follow-on screens seed example metadata URLs and default attribute names.&lt;br /&gt;
&lt;br /&gt;
Provide &#039;&#039;&#039;either&#039;&#039;&#039; an HTTPS federation metadata URL &#039;&#039;&#039;or&#039;&#039;&#039; a local federation XML absolute path — choose exactly one. Later panels cover attribute options (&#039;&#039;friendlyName&#039;&#039;, &#039;&#039;valuePath&#039;&#039;, &#039;&#039;stripPrefix&#039;&#039;, &#039;&#039;attribute.user&#039;&#039;, &#039;&#039;attribute.displayName&#039;&#039;, &#039;&#039;ForceAuthn&#039;&#039;, &#039;&#039;RequestedAuthnContext&#039;&#039;, NameID format).&lt;br /&gt;
&lt;br /&gt;
===== Group → Role Mapping =====&lt;br /&gt;
&lt;br /&gt;
The closing panels map IdP group attribute values to Obsidian roles, including &#039;&#039;&#039;Require role assignment&#039;&#039;&#039; (default yes) and optional &#039;&#039;dynamicRole.prefix&#039;&#039; / &#039;&#039;suffix&#039;&#039;. At least one mapping to the &#039;&#039;&#039;Admin&#039;&#039;&#039; role is required. See [[Advanced_Configuration#SAML_Authentication_Properties|SAML Authentication Properties]] for the full property reference.&lt;br /&gt;
&lt;br /&gt;
===== SP metadata artefacts =====&lt;br /&gt;
&lt;br /&gt;
During install the tooling writes &#039;&#039;&#039;sp-metadata.xml&#039;&#039;&#039; under the Obsidian deployment directory; the same metadata is available live at &#039;&#039;&#039;/saml/metadata&#039;&#039;&#039; after startup. Register whichever artefact your IdP admin prefers.&lt;br /&gt;
&lt;br /&gt;
===== Before you complete install =====&lt;br /&gt;
&lt;br /&gt;
* Ensure ACS and Entity ID &#039;&#039;&#039;https&#039;&#039;&#039; URLs are reachable from IdPs (align with reverse proxies).&lt;br /&gt;
* For Entra &#039;&#039;&#039;150+&#039;&#039;&#039; group memberships in SAML assertions, expect attribute suppression — filter groups or map assigned app roles instead. (OIDC uses a &#039;&#039;&#039;200&#039;&#039;&#039; group threshold; the numbers differ by protocol.)&lt;br /&gt;
&lt;br /&gt;
==== LDAP Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.LDAP.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve selected LDAP Authentication, this screen takes you through configuring the server address and the various elements used to grant access. You should familiarize yourself with Obsidian [[Authenticator#Roles|Roles]]. The &#039;&#039;Access DN&#039;&#039; configuration element grants Read access to Obsidian application. You may use the same DN for more than one Role should you so wish.&lt;br /&gt;
&lt;br /&gt;
==== Custom Authenticator Configuration====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.CustomAuthenticator.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have chosen to use your own authentication mechanism, you must enter the fully qualified classname here.&lt;br /&gt;
&lt;br /&gt;
==== Database Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Database.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Next comes database configuration. If you are using JNDI, leave the username and password fields blank. If using JDBC URL, username and password fields are required.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;Database connections per instance&#039;&#039; and &#039;&#039;Database connection timeout(millis)&#039;&#039; fields are required and provide directive to the connection pool.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Database table name prefix&#039;&#039; is optional and is typically used when Obsidian will be colocated in an existing database/schema.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Database schema (Oracle/PostgreSQL)&#039;&#039; - This is used to allow for an alternate schema other than the default user&#039;s schema. We also recommend setting this value with Oracle/PostgreSQL when using JNDI as it allows more efficient database metadata loading.&lt;br /&gt;
&lt;br /&gt;
By default, the Obsidian installation will only include the JDBC libraries necessary for your particular database. At times you may wish to include the others to be able to change between databases. In those cases, check &#039;&#039;Include all supported JDBC libs&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Email Configuration ====&lt;br /&gt;
&lt;br /&gt;
If not using JNDI for mail sessions, provide the server and authentication details as required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.SSL.Email.png]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.TLS.Email.png]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.Open.Email.png]]&lt;br /&gt;
&lt;br /&gt;
==== JNDI Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.JNDI.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have selected JNDI for mail sessions, provide the JNDI path here. If you are using JNDI for Database connections, specify the database type.&lt;br /&gt;
&lt;br /&gt;
==== Configuring 3rd Party Library Conflict Management ====&lt;br /&gt;
[[Image:JarJar-Obsidian-6.0.0.png]]&lt;br /&gt;
&lt;br /&gt;
The Obsidian installer allows you to use [https://code.google.com/archive/p/jarjar JarJar] to handle potential conflicts between Obsidian&#039;s use of 3rd party libraries and versions used within your application. On this screen, simply select which libraries to which you wish to apply the JarJar bytecode modification process. This is optional and is skipped if no libraries are selected. Any jars not listed for which you are using more recent versions can be used and are therefore not included as options in the JarJar processing.&lt;br /&gt;
&lt;br /&gt;
==== Selecting Script Libraries ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-6.0.0-Choose-Scripting-Libraries.png]]&lt;br /&gt;
&lt;br /&gt;
The Obsidian installer allows you to select which script libraries you wish to include. Jython and JRuby are incompatible with each other, so the installer will only allow one of the two selected. If all script libraries are deselected, the only script jobs that can be run in Obsidian will be Javascript jobs as that engine is built into Java.&lt;br /&gt;
&lt;br /&gt;
==== Choosing Email Support ====&lt;br /&gt;
As of Obsidian 5.2.0, you can choose between JavaMail (javax) and Jakarta mail (Jakarta EE) implementations. When choosing Jakarta, you can also choose to bundle the Angus Jakarta compatible implementation. &lt;br /&gt;
&lt;br /&gt;
If you are using JNDI, this will only include the relevant support in the Obsidian WAR artifacts and the actual libraries in use must be provided by you and bundled with your container. If the standalone scheduler is selected along with JNDI, JNDI is assumed only relevant for WARs. As such, default libraries for either JavaMail or Jakarta will be included in the standalone installation.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.EmailImplementation.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
==== Additional configuration items ====&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 5.5.0, if you require any additional configuration items such as additional appenders/loggers or event hook configurations, you can add them here.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.5.0.Extra.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
==== Offline or restricted-network install ====&lt;br /&gt;
&lt;br /&gt;
Obsidian 7.x.x resolves third-party libraries when you run the installer. For environments without direct access to Maven Central:&lt;br /&gt;
&lt;br /&gt;
# Pre-stage a local dependency cache containing every coordinate listed in the Obsidian dependency inventory for your release (contact Carfey support or your account team for the checklist file matching &#039;&#039;&#039;Obsidian-Install-7.x.x.jar&#039;&#039;&#039;).&lt;br /&gt;
# Copy the cache to the install host. Two layouts are supported:&lt;br /&gt;
#* &#039;&#039;&#039;Flat:&#039;&#039;&#039; one file per installed JAR name directly under the cache root.&lt;br /&gt;
#* &#039;&#039;&#039;File Maven repo:&#039;&#039;&#039; &#039;&#039;&#039;&amp;lt;group&amp;gt;/&amp;lt;artifact&amp;gt;/&amp;lt;version&amp;gt;/&amp;lt;installed-filename&amp;gt;.jar&#039;&#039;&#039;&lt;br /&gt;
# Run the installer with offline mode and the cache directory:&lt;br /&gt;
&lt;br /&gt;
 java -Dobsidian.install.deps.offline=true -Dobsidian.install.deps.cache.dir=/path/to/cache -jar Obsidian-Install-7.0.0.jar&lt;br /&gt;
&lt;br /&gt;
If a required library is missing from the cache, the install stops with an error naming the coordinate — add that artifact to the cache and re-run.&lt;br /&gt;
&lt;br /&gt;
===== Corporate Maven mirror (online) =====&lt;br /&gt;
&lt;br /&gt;
When your site mirrors Maven Central, point the installer at your repository base URL:&lt;br /&gt;
&lt;br /&gt;
 java -Dobsidian.install.deps.repo.url=https://nexus.example.com/repository/maven-public/ -jar Obsidian-Install-7.0.0.jar&lt;br /&gt;
&lt;br /&gt;
The installer reuses resolved artifacts for the duration of one install session.&lt;br /&gt;
&lt;br /&gt;
== Completing the Installation ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Finished.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Once you&#039;ve completed the Installation and Configuration screens, fully configured Obsidian Scheduler artifacts are now ready for you to use in the installation path you selected.&lt;br /&gt;
&lt;br /&gt;
Should you wish to automate future installations with the same configuration, click &#039;&#039;Generate an automatic installation script&#039;&#039;. This will prompt you to save an XML file that can be used for future installations using the automated install procedure &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar my-obsidian-configuration.xml&amp;lt;/code&amp;gt;. Every effort is made to ensure compatibility of these automated install files between versions. Any incompatibility will be noted in the [[Release_Notes|Release Notes]]. These automated installer files can also be used as templates for other environments, modifying them as necessary.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Installation_Guide&amp;diff=4172</id>
		<title>Installation Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Installation_Guide&amp;diff=4172"/>
		<updated>2026-08-02T02:25:43Z</updated>

		<summary type="html">&lt;p&gt;Craig: /* Provider-Specific Settings */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;This installation guide generally applies to Obsidian versions 4.0 and newer but is specific to 7.0. You may see a few minor differences in 4.x, 5.x and 6.x releases.&lt;br /&gt;
&lt;br /&gt;
This installation guide is a companion to the Obsidian Installer UI. Its purpose is to provide additional detail as to the meaning of various inputs and to provide guidance on advanced usage of the installer.&lt;br /&gt;
&lt;br /&gt;
The installer itself installs and configures the artifacts. The configured artifacts will have their [[Advanced_Configuration#Properties_File|Properties]] file configured according to the choices made during the installation process. You may always change these later or use one of the [[Advanced_Configuration#Properties_File|override]] mechanisms.&lt;br /&gt;
== Running the Obsidian Installer ==&lt;br /&gt;
The installer is an executable JAR file in the Obsidian download zip package available on our [https://web.obsidianscheduler.com/download/ download page].&lt;br /&gt;
&lt;br /&gt;
The installer can be run from the command line as a graphical user interface using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar&amp;lt;/code&amp;gt; or in interactive console mode using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar -console&amp;lt;/code&amp;gt;. Note that you will have to replace the JAR file name with the actual versioned name in your installation.&lt;br /&gt;
&lt;br /&gt;
On some platforms, simple double-clicking the JAR file will start it in graphical interface mode.&lt;br /&gt;
&lt;br /&gt;
If you are doing a version upgrade of Obsidian or are otherwise uninterested in actually configuring the artifacts, you can run the quick start mode to get the default configured artifacts using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar h2-tomcat-quick-start.xml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Obsidian Installer Artifacts ==&lt;br /&gt;
&lt;br /&gt;
The Obsidian Installer installs and configures a number of artifacts. You can choose which artifacts to create, but most users can leave the default options selected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Installation-Packages.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Obsidian WAR&#039;&#039;&#039; - This is the Obsidian Web Admin UI with scheduler component enabled. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/obsidian.war!/WEB-INF/classes/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Standalone Admin WAR&#039;&#039;&#039; - This is the Obsidian Web Admin UI with scheduler component disabled. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/standaloneObsidianAdmin.war!/WEB-INF/classes/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Standalone Obsidian Runtime&#039;&#039;&#039; - This is a runtime folder containing the libraries and configuration necessary for running the Obsidian Scheduler component either as a standalone module or for use as an Embedded Scheduler in your application. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/standalone/obsidian-props.jar!/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian License Proxy&#039;&#039;&#039; - Allows for local license leasing from a [[Key_Server_Proxy|Key Server Proxy]] that ultimately leases licenses from the Obsidian License server.&lt;br /&gt;
# &#039;&#039;&#039;Documentation&#039;&#039;&#039; - README and Embedded API Javadoc.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; — An embedded Apache Tomcat runtime for running Obsidian locally without an external servlet container. Requires the Obsidian WAR pack.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; — An embedded Apache Tomcat runtime for running the standalone admin WAR locally. Requires the Obsidian Standalone Admin WAR pack.&lt;br /&gt;
# &#039;&#039;&#039;License&#039;&#039;&#039; - Obsidian and 3rd party license information.&lt;br /&gt;
&lt;br /&gt;
==== Deployment options ====&lt;br /&gt;
&lt;br /&gt;
Obsidian 7 offers four deployment profiles in the installer:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Obsidian WAR&#039;&#039;&#039; — deploy &#039;&#039;&#039;obsidian.war&#039;&#039;&#039; to your own servlet container (Tomcat, WebLogic, etc.).&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; — adds an embedded Tomcat runtime and &#039;&#039;&#039;webObsidian&#039;&#039;&#039; scripts to run &#039;&#039;&#039;obsidian.war&#039;&#039;&#039; locally (&#039;&#039;&#039;start scheduler&#039;&#039;&#039;). &#039;&#039;&#039;Requires&#039;&#039;&#039; the Obsidian WAR pack.&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Standalone Admin WAR&#039;&#039;&#039; — admin console WAR for an external servlet container.&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; — embedded runtime for the admin WAR (&#039;&#039;&#039;start adminOnly&#039;&#039;&#039;). &#039;&#039;&#039;Requires&#039;&#039;&#039; the Standalone Admin WAR pack.&lt;br /&gt;
&lt;br /&gt;
Embedded Tomcat does &#039;&#039;&#039;not&#039;&#039;&#039; replace the WAR on disk — it runs the installed WAR via the helper. Do not select an embed pack without its matching WAR pack. For how to start, stop and change the ports of the embedded runtime, see [[Getting_Started#Starting_Obsidian_with_embedded_Tomcat|Starting Obsidian with embedded Tomcat]].&lt;br /&gt;
&lt;br /&gt;
==== Linux desktop and systemd integration ====&lt;br /&gt;
&lt;br /&gt;
On a Unix host, an embedded Tomcat pack also copies a &#039;&#039;&#039;linux/&#039;&#039;&#039; directory under your install path holding user-level systemd and desktop-entry templates, and the installer offers to activate them. Nothing is installed system-wide and no root privileges are required.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Embedded Tomcat - Linux integration (optional)&#039;&#039;&#039; panel carries the checkbox &#039;&#039;&#039;Install Linux desktop and user systemd integration&#039;&#039;&#039; (unchecked by default). Tick it to have the installer run the integration script for you; the option only takes effect if you also selected an embedded Tomcat pack. Leaving it unchecked still copies the templates, so you can opt in later by running this from the install directory:&lt;br /&gt;
&lt;br /&gt;
 ./linux/install-embedded-linux-integration.sh &amp;quot;$PWD&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Either route writes two files, for the installing user only:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/.config/systemd/user/obsidian-embedded@scheduler.service&#039;&#039;&lt;br /&gt;
* &#039;&#039;~/.local/share/applications/obsidian-embedded.desktop&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;XDG_CONFIG_HOME&#039;&#039; and &#039;&#039;XDG_DATA_HOME&#039;&#039; are honoured when set.)&lt;br /&gt;
&lt;br /&gt;
For an automated (headless) install, request the integration with:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;entry key=&amp;quot;embed.linux.integration&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This panel is &#039;&#039;&#039;not shown on Windows installs&#039;&#039;&#039; — the systemd and desktop templates are Linux-only. On Windows, run &#039;&#039;&#039;webObsidian.bat&#039;&#039;&#039; manually or wrap it with your own service tooling.&lt;br /&gt;
&lt;br /&gt;
Installing these files does not enable or start anything. To enable, stop, reconfigure or remove the service, see [[Getting_Started#Running_Obsidian_as_a_Linux_user_service|Running Obsidian as a Linux user service]].&lt;br /&gt;
&lt;br /&gt;
== Obsidian Configuration ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Obsidian.png]]&lt;br /&gt;
&lt;br /&gt;
First, you must choose what [[Authenticator]] mechanism will be used within Obsidian. Obsidian supports five authentication methods: native (database), LDAP, OAuth (OIDC), SAML 2.0, and custom. Select &#039;&#039;Native (Database)&#039;&#039;, &#039;&#039;LDAP&#039;&#039;, &#039;&#039;OAuth (OIDC)&#039;&#039;, &#039;&#039;SAML 2.0&#039;&#039;, or &#039;&#039;Other&#039;&#039; (custom). &#039;&#039;Native (Database)&#039;&#039; requires no additional configuration and is what most users will select at least to start.&lt;br /&gt;
&lt;br /&gt;
Next, you&#039;ll want to select the email usage type. We highly recommend you configure Obsidian for email use as it will allow you to benefit from the event [[Event_Notifications|notification]] and [[Admin_Notifications|subscription]] support in Obsidian.&lt;br /&gt;
&lt;br /&gt;
Then you&#039;ll configure the log file location, license key (optional) and registered company name (optional). Registered company name is only required for our Site License users.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== OAuth / OIDC Configuration ====&lt;br /&gt;
&lt;br /&gt;
If you selected &#039;&#039;&#039;OAuth (OIDC)&#039;&#039;&#039; as the authentication type, the installer displays additional panels for OAuth configuration.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Provider and Common Settings =====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-OAuth_OIDC.png]]&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Identity Provider&#039;&#039;&#039; — Select your provider: Keycloak, Microsoft Entra ID, Okta, Auth0, or Generic OIDC.&lt;br /&gt;
* &#039;&#039;&#039;SSO Button label&#039;&#039;&#039; — Label shown on the &amp;quot;Sign in with…&amp;quot; button (e.g. &#039;&#039;Keycloak&#039;&#039;, &#039;&#039;Acme Corp SSO&#039;&#039;). If left blank the button reads &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
* &#039;&#039;&#039;Redirect URI&#039;&#039;&#039; — The callback URL Obsidian receives after IdP login. Must be registered at the IdP (e.g. &#039;&#039;https://app.example.com/oauth/callback&#039;&#039;).&lt;br /&gt;
* &#039;&#039;&#039;Logout mode&#039;&#039;&#039; — &#039;&#039;local&#039;&#039; (default) or &#039;&#039;rp&#039;&#039; (RP-initiated logout via the IdP&#039;s end_session_endpoint).&lt;br /&gt;
&lt;br /&gt;
===== Provider-Specific Settings =====&lt;br /&gt;
&lt;br /&gt;
Fill in the connection details for your chosen provider.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Keycloak.png]]&lt;br /&gt;
&lt;br /&gt;
;Keycloak&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — e.g. &#039;&#039;https://keycloak.example.com/realms/myrealm&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from the Keycloak client settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — recommended: &#039;&#039;openid profile email groups&#039;&#039;. &lt;br /&gt;
Note: Keycloak does not include a &#039;&#039;groups&#039;&#039; claim by default — a Group Membership mapper must be configured in the client&#039;s Mappers tab.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Microsoft-Entra-ID.png]]&lt;br /&gt;
&lt;br /&gt;
;Microsoft Entra ID&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — &#039;&#039;https://login.microsoftonline.com/&amp;lt;tenant-id&amp;gt;/v2.0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; — the Application (client) ID from the Azure portal.&lt;br /&gt;
* &#039;&#039;&#039;Client Secret&#039;&#039;&#039; — a client secret value from the Azure portal.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email&#039;&#039;. &lt;br /&gt;
Note: the &#039;&#039;groups&#039;&#039; claim contains GUIDs by default; configure Optional Claims in the Azure portal to receive group display names. &lt;br /&gt;
&#039;&#039;&#039;Multi-tenant applications are not supported in Obsidian 7.0.x.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Okta.png]]&lt;br /&gt;
&lt;br /&gt;
;Okta&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — use the &#039;&#039;&#039;Custom Authorization Server&#039;&#039;&#039; URL, e.g. &#039;&#039;https://&amp;lt;domain&amp;gt;.okta.com/oauth2/default&#039;&#039;. Do &#039;&#039;&#039;not&#039;&#039;&#039; use the Org Authorization Server URL.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from Okta application settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email groups&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Auth0.png]]&lt;br /&gt;
&lt;br /&gt;
;Auth0&lt;br /&gt;
* &#039;&#039;&#039;Domain / Issuer URL&#039;&#039;&#039; — e.g. &#039;&#039;https://&amp;lt;domain&amp;gt;.auth0.com/&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from Auth0 application settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email&#039;&#039;.&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; Auth0 issues opaque access tokens by default which are &#039;&#039;&#039;not supported&#039;&#039;&#039; on Obsidian&#039;s REST/Bearer path — configure a custom API in Auth0 to get JWT access tokens.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Generic_OIDC.png]]&lt;br /&gt;
&lt;br /&gt;
;Generic OIDC&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — the base URL; Obsidian appends &#039;&#039;/.well-known/openid-configuration&#039;&#039; to discover endpoints.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from the provider.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — at minimum &#039;&#039;openid&#039;&#039;; add &#039;&#039;profile&#039;&#039; and your groups scope as needed.&lt;br /&gt;
&lt;br /&gt;
===== Group → Role Mapping =====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-OAuth-Group-Role-Mapping.png]]&lt;br /&gt;
&lt;br /&gt;
The final OAuth panel maps IdP group values to Obsidian roles. &#039;&#039;&#039;Require role assignment&#039;&#039;&#039; (default yes) — when enabled, users with no matching roles cannot complete browser login. At least one mapping to the &#039;&#039;&#039;Admin&#039;&#039;&#039; role is required. The installer supports up to 10 group→role pairs; additional pairs can be added directly to the configuration file after install. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties]] for the full property reference.&lt;br /&gt;
&lt;br /&gt;
===== Registering the redirect URI at the IdP =====&lt;br /&gt;
&lt;br /&gt;
Before starting Obsidian, register the redirect URI shown in the installer at your IdP: Keycloak — Client → Settings → Valid Redirect URIs; Entra ID — App Registration → Authentication → Redirect URIs; Okta — Application → General Settings → Sign-in Redirect URIs; Auth0 — Application → Settings → Allowed Callback URLs. The URI must match &#039;&#039;com.carfey.suite.security.OAuthAuthenticator.redirectUri&#039;&#039; exactly, including scheme and port.&lt;br /&gt;
&lt;br /&gt;
==== SAML 2.0 Configuration ====&lt;br /&gt;
&lt;br /&gt;
If you selected &#039;&#039;&#039;SAML 2.0&#039;&#039;&#039; as the authentication type, the installer displays additional panels that configure the Service Provider identity, SAML signing credentials, federation metadata, attribute options, and group→role mapping.&lt;br /&gt;
&lt;br /&gt;
===== Service Provider =====&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SP Entity ID&#039;&#039;&#039; — HTTPS URI registered as the SAML SP entity (must match IdP registrations).&lt;br /&gt;
* &#039;&#039;&#039;Assertion Consumer Service URL&#039;&#039;&#039; — HTTPS ACS endpoint where SAML responses arrive (HTTP-POST); aligns with the ACS in &#039;&#039;/saml/metadata&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;SP Single Logout Service URL&#039;&#039;&#039; — Optional HTTPS URL for IdP-initiated SLO when logout mode targets IdP-mediated logout.&lt;br /&gt;
* &#039;&#039;&#039;SSO button label&#039;&#039;&#039; — Required label shown on &#039;&#039;&#039;Sign in with …&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Logout mode&#039;&#039;&#039; — &#039;&#039;&#039;Local&#039;&#039;&#039; (Obsidian-only) or &#039;&#039;&#039;IdP / SLO&#039;&#039;&#039; (participates in SAML single logout when advertised).&lt;br /&gt;
* &#039;&#039;&#039;Post-logout redirect URI&#039;&#039;&#039; — Optional HTTPS landing URI after &#039;&#039;LogoutResponse&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Keystore path / alias / passwords&#039;&#039;&#039; — Required when signing SAML requests (password field is dual-entry validated).&lt;br /&gt;
* &#039;&#039;&#039;Sign SAML authentication requests&#039;&#039;&#039; — Toggle (installer default &#039;&#039;&#039;true&#039;&#039;&#039;).&lt;br /&gt;
* &#039;&#039;&#039;Require signed SAML assertions&#039;&#039;&#039; — Toggle (recommended &#039;&#039;&#039;true&#039;&#039;&#039; for production).&lt;br /&gt;
&lt;br /&gt;
===== Identity Provider =====&lt;br /&gt;
&lt;br /&gt;
Choose the IdP type: Microsoft Entra ID (Azure AD), AD FS (on-premises), Okta, Auth0, or Generic SAML 2.0 (covers Keycloak &#039;&#039;/protocol/saml/descriptor&#039;&#039;, Ping, Google Workspace, and similar). Follow-on screens seed example metadata URLs and default attribute names.&lt;br /&gt;
&lt;br /&gt;
Provide &#039;&#039;&#039;either&#039;&#039;&#039; an HTTPS federation metadata URL &#039;&#039;&#039;or&#039;&#039;&#039; a local federation XML absolute path — choose exactly one. Later panels cover attribute options (&#039;&#039;friendlyName&#039;&#039;, &#039;&#039;valuePath&#039;&#039;, &#039;&#039;stripPrefix&#039;&#039;, &#039;&#039;attribute.user&#039;&#039;, &#039;&#039;attribute.displayName&#039;&#039;, &#039;&#039;ForceAuthn&#039;&#039;, &#039;&#039;RequestedAuthnContext&#039;&#039;, NameID format).&lt;br /&gt;
&lt;br /&gt;
===== Group → Role Mapping =====&lt;br /&gt;
&lt;br /&gt;
The closing panels map IdP group attribute values to Obsidian roles, including &#039;&#039;&#039;Require role assignment&#039;&#039;&#039; (default yes) and optional &#039;&#039;dynamicRole.prefix&#039;&#039; / &#039;&#039;suffix&#039;&#039;. At least one mapping to the &#039;&#039;&#039;Admin&#039;&#039;&#039; role is required. See [[Advanced_Configuration#SAML_Authentication_Properties|SAML Authentication Properties]] for the full property reference.&lt;br /&gt;
&lt;br /&gt;
===== SP metadata artefacts =====&lt;br /&gt;
&lt;br /&gt;
During install the tooling writes &#039;&#039;&#039;sp-metadata.xml&#039;&#039;&#039; under the Obsidian deployment directory; the same metadata is available live at &#039;&#039;&#039;/saml/metadata&#039;&#039;&#039; after startup. Register whichever artefact your IdP admin prefers.&lt;br /&gt;
&lt;br /&gt;
===== Before you complete install =====&lt;br /&gt;
&lt;br /&gt;
* Ensure ACS and Entity ID &#039;&#039;&#039;https&#039;&#039;&#039; URLs are reachable from IdPs (align with reverse proxies).&lt;br /&gt;
* For Entra &#039;&#039;&#039;150+&#039;&#039;&#039; group memberships in SAML assertions, expect attribute suppression — filter groups or map assigned app roles instead. (OIDC uses a &#039;&#039;&#039;200&#039;&#039;&#039; group threshold; the numbers differ by protocol.)&lt;br /&gt;
&lt;br /&gt;
==== LDAP Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.LDAP.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve selected LDAP Authentication, this screen takes you through configuring the server address and the various elements used to grant access. You should familiarize yourself with Obsidian [[Authenticator#Roles|Roles]]. The &#039;&#039;Access DN&#039;&#039; configuration element grants Read access to Obsidian application. You may use the same DN for more than one Role should you so wish.&lt;br /&gt;
&lt;br /&gt;
==== Custom Authenticator Configuration====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.CustomAuthenticator.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have chosen to use your own authentication mechanism, you must enter the fully qualified classname here.&lt;br /&gt;
&lt;br /&gt;
==== Database Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Database.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Next comes database configuration. If you are using JNDI, leave the username and password fields blank. If using JDBC URL, username and password fields are required.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;Database connections per instance&#039;&#039; and &#039;&#039;Database connection timeout(millis)&#039;&#039; fields are required and provide directive to the connection pool.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Database table name prefix&#039;&#039; is optional and is typically used when Obsidian will be colocated in an existing database/schema.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Database schema (Oracle/PostgreSQL)&#039;&#039; - This is used to allow for an alternate schema other than the default user&#039;s schema. We also recommend setting this value with Oracle/PostgreSQL when using JNDI as it allows more efficient database metadata loading.&lt;br /&gt;
&lt;br /&gt;
By default, the Obsidian installation will only include the JDBC libraries necessary for your particular database. At times you may wish to include the others to be able to change between databases. In those cases, check &#039;&#039;Include all supported JDBC libs&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Email Configuration ====&lt;br /&gt;
&lt;br /&gt;
If not using JNDI for mail sessions, provide the server and authentication details as required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.SSL.Email.png]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.TLS.Email.png]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.Open.Email.png]]&lt;br /&gt;
&lt;br /&gt;
==== JNDI Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.JNDI.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have selected JNDI for mail sessions, provide the JNDI path here. If you are using JNDI for Database connections, specify the database type.&lt;br /&gt;
&lt;br /&gt;
==== Configuring 3rd Party Library Conflict Management ====&lt;br /&gt;
[[Image:JarJar-Obsidian-6.0.0.png]]&lt;br /&gt;
&lt;br /&gt;
The Obsidian installer allows you to use [https://code.google.com/archive/p/jarjar JarJar] to handle potential conflicts between Obsidian&#039;s use of 3rd party libraries and versions used within your application. On this screen, simply select which libraries to which you wish to apply the JarJar bytecode modification process. This is optional and is skipped if no libraries are selected. Any jars not listed for which you are using more recent versions can be used and are therefore not included as options in the JarJar processing.&lt;br /&gt;
&lt;br /&gt;
==== Selecting Script Libraries ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-6.0.0-Choose-Scripting-Libraries.png]]&lt;br /&gt;
&lt;br /&gt;
The Obsidian installer allows you to select which script libraries you wish to include. Jython and JRuby are incompatible with each other, so the installer will only allow one of the two selected. If all script libraries are deselected, the only script jobs that can be run in Obsidian will be Javascript jobs as that engine is built into Java.&lt;br /&gt;
&lt;br /&gt;
==== Choosing Email Support ====&lt;br /&gt;
As of Obsidian 5.2.0, you can choose between JavaMail (javax) and Jakarta mail (Jakarta EE) implementations. When choosing Jakarta, you can also choose to bundle the Angus Jakarta compatible implementation. &lt;br /&gt;
&lt;br /&gt;
If you are using JNDI, this will only include the relevant support in the Obsidian WAR artifacts and the actual libraries in use must be provided by you and bundled with your container. If the standalone scheduler is selected along with JNDI, JNDI is assumed only relevant for WARs. As such, default libraries for either JavaMail or Jakarta will be included in the standalone installation.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.EmailImplementation.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
==== Additional configuration items ====&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 5.5.0, if you require any additional configuration items such as additional appenders/loggers or event hook configurations, you can add them here.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.5.0.Extra.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
==== Offline or restricted-network install ====&lt;br /&gt;
&lt;br /&gt;
Obsidian 7.0.0 resolves third-party libraries when you run the installer. For environments without direct access to Maven Central:&lt;br /&gt;
&lt;br /&gt;
# Pre-stage a local dependency cache containing every coordinate listed in the Obsidian dependency inventory for your release (contact Carfey support or your account team for the checklist file matching &#039;&#039;&#039;Obsidian-Install-7.0.0.jar&#039;&#039;&#039;).&lt;br /&gt;
# Copy the cache to the install host. Two layouts are supported:&lt;br /&gt;
#* &#039;&#039;&#039;Flat:&#039;&#039;&#039; one file per installed JAR name directly under the cache root.&lt;br /&gt;
#* &#039;&#039;&#039;File Maven repo:&#039;&#039;&#039; &#039;&#039;&#039;&amp;lt;group&amp;gt;/&amp;lt;artifact&amp;gt;/&amp;lt;version&amp;gt;/&amp;lt;installed-filename&amp;gt;.jar&#039;&#039;&#039;&lt;br /&gt;
# Run the installer with offline mode and the cache directory:&lt;br /&gt;
&lt;br /&gt;
 java -Dobsidian.install.deps.offline=true -Dobsidian.install.deps.cache.dir=/path/to/cache -jar Obsidian-Install-7.0.0.jar&lt;br /&gt;
&lt;br /&gt;
If a required library is missing from the cache, the install stops with an error naming the coordinate — add that artifact to the cache and re-run.&lt;br /&gt;
&lt;br /&gt;
===== Corporate Maven mirror (online) =====&lt;br /&gt;
&lt;br /&gt;
When your site mirrors Maven Central, point the installer at your repository base URL:&lt;br /&gt;
&lt;br /&gt;
 java -Dobsidian.install.deps.repo.url=https://nexus.example.com/repository/maven-public/ -jar Obsidian-Install-7.0.0.jar&lt;br /&gt;
&lt;br /&gt;
The installer reuses resolved artifacts for the duration of one install session.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Completing the Installation ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Finished.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Once you&#039;ve completed the Installation and Configuration screens, fully configured Obsidian Scheduler artifacts are now ready for you to use in the installation path you selected.&lt;br /&gt;
&lt;br /&gt;
Should you wish to automate future installations with the same configuration, click &#039;&#039;Generate an automatic installation script&#039;&#039;. This will prompt you to save an XML file that can be used for future installations using the automated install procedure &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar my-obsidian-configuration.xml&amp;lt;/code&amp;gt;. Every effort is made to ensure compatibility of these automated install files between versions. Any incompatibility will be noted in the [[Release_Notes|Release Notes]]. These automated installer files can also be used as templates for other environments, modifying them as necessary.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Installation_Guide&amp;diff=4171</id>
		<title>Installation Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Installation_Guide&amp;diff=4171"/>
		<updated>2026-08-02T02:25:20Z</updated>

		<summary type="html">&lt;p&gt;Craig: /* Deployment options */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;This installation guide generally applies to Obsidian versions 4.0 and newer but is specific to 7.0. You may see a few minor differences in 4.x, 5.x and 6.x releases.&lt;br /&gt;
&lt;br /&gt;
This installation guide is a companion to the Obsidian Installer UI. Its purpose is to provide additional detail as to the meaning of various inputs and to provide guidance on advanced usage of the installer.&lt;br /&gt;
&lt;br /&gt;
The installer itself installs and configures the artifacts. The configured artifacts will have their [[Advanced_Configuration#Properties_File|Properties]] file configured according to the choices made during the installation process. You may always change these later or use one of the [[Advanced_Configuration#Properties_File|override]] mechanisms.&lt;br /&gt;
== Running the Obsidian Installer ==&lt;br /&gt;
The installer is an executable JAR file in the Obsidian download zip package available on our [https://web.obsidianscheduler.com/download/ download page].&lt;br /&gt;
&lt;br /&gt;
The installer can be run from the command line as a graphical user interface using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar&amp;lt;/code&amp;gt; or in interactive console mode using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar -console&amp;lt;/code&amp;gt;. Note that you will have to replace the JAR file name with the actual versioned name in your installation.&lt;br /&gt;
&lt;br /&gt;
On some platforms, simple double-clicking the JAR file will start it in graphical interface mode.&lt;br /&gt;
&lt;br /&gt;
If you are doing a version upgrade of Obsidian or are otherwise uninterested in actually configuring the artifacts, you can run the quick start mode to get the default configured artifacts using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar h2-tomcat-quick-start.xml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Obsidian Installer Artifacts ==&lt;br /&gt;
&lt;br /&gt;
The Obsidian Installer installs and configures a number of artifacts. You can choose which artifacts to create, but most users can leave the default options selected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Installation-Packages.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Obsidian WAR&#039;&#039;&#039; - This is the Obsidian Web Admin UI with scheduler component enabled. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/obsidian.war!/WEB-INF/classes/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Standalone Admin WAR&#039;&#039;&#039; - This is the Obsidian Web Admin UI with scheduler component disabled. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/standaloneObsidianAdmin.war!/WEB-INF/classes/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Standalone Obsidian Runtime&#039;&#039;&#039; - This is a runtime folder containing the libraries and configuration necessary for running the Obsidian Scheduler component either as a standalone module or for use as an Embedded Scheduler in your application. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/standalone/obsidian-props.jar!/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian License Proxy&#039;&#039;&#039; - Allows for local license leasing from a [[Key_Server_Proxy|Key Server Proxy]] that ultimately leases licenses from the Obsidian License server.&lt;br /&gt;
# &#039;&#039;&#039;Documentation&#039;&#039;&#039; - README and Embedded API Javadoc.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; — An embedded Apache Tomcat runtime for running Obsidian locally without an external servlet container. Requires the Obsidian WAR pack.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; — An embedded Apache Tomcat runtime for running the standalone admin WAR locally. Requires the Obsidian Standalone Admin WAR pack.&lt;br /&gt;
# &#039;&#039;&#039;License&#039;&#039;&#039; - Obsidian and 3rd party license information.&lt;br /&gt;
&lt;br /&gt;
==== Deployment options ====&lt;br /&gt;
&lt;br /&gt;
Obsidian 7 offers four deployment profiles in the installer:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Obsidian WAR&#039;&#039;&#039; — deploy &#039;&#039;&#039;obsidian.war&#039;&#039;&#039; to your own servlet container (Tomcat, WebLogic, etc.).&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; — adds an embedded Tomcat runtime and &#039;&#039;&#039;webObsidian&#039;&#039;&#039; scripts to run &#039;&#039;&#039;obsidian.war&#039;&#039;&#039; locally (&#039;&#039;&#039;start scheduler&#039;&#039;&#039;). &#039;&#039;&#039;Requires&#039;&#039;&#039; the Obsidian WAR pack.&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Standalone Admin WAR&#039;&#039;&#039; — admin console WAR for an external servlet container.&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; — embedded runtime for the admin WAR (&#039;&#039;&#039;start adminOnly&#039;&#039;&#039;). &#039;&#039;&#039;Requires&#039;&#039;&#039; the Standalone Admin WAR pack.&lt;br /&gt;
&lt;br /&gt;
Embedded Tomcat does &#039;&#039;&#039;not&#039;&#039;&#039; replace the WAR on disk — it runs the installed WAR via the helper. Do not select an embed pack without its matching WAR pack. For how to start, stop and change the ports of the embedded runtime, see [[Getting_Started#Starting_Obsidian_with_embedded_Tomcat|Starting Obsidian with embedded Tomcat]].&lt;br /&gt;
&lt;br /&gt;
==== Linux desktop and systemd integration ====&lt;br /&gt;
&lt;br /&gt;
On a Unix host, an embedded Tomcat pack also copies a &#039;&#039;&#039;linux/&#039;&#039;&#039; directory under your install path holding user-level systemd and desktop-entry templates, and the installer offers to activate them. Nothing is installed system-wide and no root privileges are required.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Embedded Tomcat - Linux integration (optional)&#039;&#039;&#039; panel carries the checkbox &#039;&#039;&#039;Install Linux desktop and user systemd integration&#039;&#039;&#039; (unchecked by default). Tick it to have the installer run the integration script for you; the option only takes effect if you also selected an embedded Tomcat pack. Leaving it unchecked still copies the templates, so you can opt in later by running this from the install directory:&lt;br /&gt;
&lt;br /&gt;
 ./linux/install-embedded-linux-integration.sh &amp;quot;$PWD&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Either route writes two files, for the installing user only:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/.config/systemd/user/obsidian-embedded@scheduler.service&#039;&#039;&lt;br /&gt;
* &#039;&#039;~/.local/share/applications/obsidian-embedded.desktop&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;XDG_CONFIG_HOME&#039;&#039; and &#039;&#039;XDG_DATA_HOME&#039;&#039; are honoured when set.)&lt;br /&gt;
&lt;br /&gt;
For an automated (headless) install, request the integration with:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;entry key=&amp;quot;embed.linux.integration&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This panel is &#039;&#039;&#039;not shown on Windows installs&#039;&#039;&#039; — the systemd and desktop templates are Linux-only. On Windows, run &#039;&#039;&#039;webObsidian.bat&#039;&#039;&#039; manually or wrap it with your own service tooling.&lt;br /&gt;
&lt;br /&gt;
Installing these files does not enable or start anything. To enable, stop, reconfigure or remove the service, see [[Getting_Started#Running_Obsidian_as_a_Linux_user_service|Running Obsidian as a Linux user service]].&lt;br /&gt;
&lt;br /&gt;
== Obsidian Configuration ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Obsidian.png]]&lt;br /&gt;
&lt;br /&gt;
First, you must choose what [[Authenticator]] mechanism will be used within Obsidian. Obsidian supports five authentication methods: native (database), LDAP, OAuth (OIDC), SAML 2.0, and custom. Select &#039;&#039;Native (Database)&#039;&#039;, &#039;&#039;LDAP&#039;&#039;, &#039;&#039;OAuth (OIDC)&#039;&#039;, &#039;&#039;SAML 2.0&#039;&#039;, or &#039;&#039;Other&#039;&#039; (custom). &#039;&#039;Native (Database)&#039;&#039; requires no additional configuration and is what most users will select at least to start.&lt;br /&gt;
&lt;br /&gt;
Next, you&#039;ll want to select the email usage type. We highly recommend you configure Obsidian for email use as it will allow you to benefit from the event [[Event_Notifications|notification]] and [[Admin_Notifications|subscription]] support in Obsidian.&lt;br /&gt;
&lt;br /&gt;
Then you&#039;ll configure the log file location, license key (optional) and registered company name (optional). Registered company name is only required for our Site License users.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== OAuth / OIDC Configuration ====&lt;br /&gt;
&lt;br /&gt;
If you selected &#039;&#039;&#039;OAuth (OIDC)&#039;&#039;&#039; as the authentication type, the installer displays additional panels for OAuth configuration.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Provider and Common Settings =====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-OAuth_OIDC.png]]&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Identity Provider&#039;&#039;&#039; — Select your provider: Keycloak, Microsoft Entra ID, Okta, Auth0, or Generic OIDC.&lt;br /&gt;
* &#039;&#039;&#039;SSO Button label&#039;&#039;&#039; — Label shown on the &amp;quot;Sign in with…&amp;quot; button (e.g. &#039;&#039;Keycloak&#039;&#039;, &#039;&#039;Acme Corp SSO&#039;&#039;). If left blank the button reads &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
* &#039;&#039;&#039;Redirect URI&#039;&#039;&#039; — The callback URL Obsidian receives after IdP login. Must be registered at the IdP (e.g. &#039;&#039;https://app.example.com/oauth/callback&#039;&#039;).&lt;br /&gt;
* &#039;&#039;&#039;Logout mode&#039;&#039;&#039; — &#039;&#039;local&#039;&#039; (default) or &#039;&#039;rp&#039;&#039; (RP-initiated logout via the IdP&#039;s end_session_endpoint).&lt;br /&gt;
&lt;br /&gt;
===== Provider-Specific Settings =====&lt;br /&gt;
&lt;br /&gt;
Fill in the connection details for your chosen provider.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Keycloak.png]]&lt;br /&gt;
&lt;br /&gt;
;Keycloak&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — e.g. &#039;&#039;https://keycloak.example.com/realms/myrealm&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from the Keycloak client settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — recommended: &#039;&#039;openid profile email groups&#039;&#039;. &lt;br /&gt;
Note: Keycloak does not include a &#039;&#039;groups&#039;&#039; claim by default — a Group Membership mapper must be configured in the client&#039;s Mappers tab.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Microsoft-Entra-ID.png]]&lt;br /&gt;
&lt;br /&gt;
;Microsoft Entra ID&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — &#039;&#039;https://login.microsoftonline.com/&amp;lt;tenant-id&amp;gt;/v2.0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; — the Application (client) ID from the Azure portal.&lt;br /&gt;
* &#039;&#039;&#039;Client Secret&#039;&#039;&#039; — a client secret value from the Azure portal.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email&#039;&#039;. &lt;br /&gt;
Note: the &#039;&#039;groups&#039;&#039; claim contains GUIDs by default; configure Optional Claims in the Azure portal to receive group display names. &lt;br /&gt;
&#039;&#039;&#039;Multi-tenant applications are not supported in Obsidian 7.0.0.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Okta.png]]&lt;br /&gt;
&lt;br /&gt;
;Okta&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — use the &#039;&#039;&#039;Custom Authorization Server&#039;&#039;&#039; URL, e.g. &#039;&#039;https://&amp;lt;domain&amp;gt;.okta.com/oauth2/default&#039;&#039;. Do &#039;&#039;&#039;not&#039;&#039;&#039; use the Org Authorization Server URL.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from Okta application settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email groups&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Auth0.png]]&lt;br /&gt;
&lt;br /&gt;
;Auth0&lt;br /&gt;
* &#039;&#039;&#039;Domain / Issuer URL&#039;&#039;&#039; — e.g. &#039;&#039;https://&amp;lt;domain&amp;gt;.auth0.com/&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from Auth0 application settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email&#039;&#039;.&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; Auth0 issues opaque access tokens by default which are &#039;&#039;&#039;not supported&#039;&#039;&#039; on Obsidian&#039;s REST/Bearer path — configure a custom API in Auth0 to get JWT access tokens.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Generic_OIDC.png]]&lt;br /&gt;
&lt;br /&gt;
;Generic OIDC&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — the base URL; Obsidian appends &#039;&#039;/.well-known/openid-configuration&#039;&#039; to discover endpoints.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from the provider.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — at minimum &#039;&#039;openid&#039;&#039;; add &#039;&#039;profile&#039;&#039; and your groups scope as needed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Group → Role Mapping =====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-OAuth-Group-Role-Mapping.png]]&lt;br /&gt;
&lt;br /&gt;
The final OAuth panel maps IdP group values to Obsidian roles. &#039;&#039;&#039;Require role assignment&#039;&#039;&#039; (default yes) — when enabled, users with no matching roles cannot complete browser login. At least one mapping to the &#039;&#039;&#039;Admin&#039;&#039;&#039; role is required. The installer supports up to 10 group→role pairs; additional pairs can be added directly to the configuration file after install. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties]] for the full property reference.&lt;br /&gt;
&lt;br /&gt;
===== Registering the redirect URI at the IdP =====&lt;br /&gt;
&lt;br /&gt;
Before starting Obsidian, register the redirect URI shown in the installer at your IdP: Keycloak — Client → Settings → Valid Redirect URIs; Entra ID — App Registration → Authentication → Redirect URIs; Okta — Application → General Settings → Sign-in Redirect URIs; Auth0 — Application → Settings → Allowed Callback URLs. The URI must match &#039;&#039;com.carfey.suite.security.OAuthAuthenticator.redirectUri&#039;&#039; exactly, including scheme and port.&lt;br /&gt;
&lt;br /&gt;
==== SAML 2.0 Configuration ====&lt;br /&gt;
&lt;br /&gt;
If you selected &#039;&#039;&#039;SAML 2.0&#039;&#039;&#039; as the authentication type, the installer displays additional panels that configure the Service Provider identity, SAML signing credentials, federation metadata, attribute options, and group→role mapping.&lt;br /&gt;
&lt;br /&gt;
===== Service Provider =====&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SP Entity ID&#039;&#039;&#039; — HTTPS URI registered as the SAML SP entity (must match IdP registrations).&lt;br /&gt;
* &#039;&#039;&#039;Assertion Consumer Service URL&#039;&#039;&#039; — HTTPS ACS endpoint where SAML responses arrive (HTTP-POST); aligns with the ACS in &#039;&#039;/saml/metadata&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;SP Single Logout Service URL&#039;&#039;&#039; — Optional HTTPS URL for IdP-initiated SLO when logout mode targets IdP-mediated logout.&lt;br /&gt;
* &#039;&#039;&#039;SSO button label&#039;&#039;&#039; — Required label shown on &#039;&#039;&#039;Sign in with …&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Logout mode&#039;&#039;&#039; — &#039;&#039;&#039;Local&#039;&#039;&#039; (Obsidian-only) or &#039;&#039;&#039;IdP / SLO&#039;&#039;&#039; (participates in SAML single logout when advertised).&lt;br /&gt;
* &#039;&#039;&#039;Post-logout redirect URI&#039;&#039;&#039; — Optional HTTPS landing URI after &#039;&#039;LogoutResponse&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Keystore path / alias / passwords&#039;&#039;&#039; — Required when signing SAML requests (password field is dual-entry validated).&lt;br /&gt;
* &#039;&#039;&#039;Sign SAML authentication requests&#039;&#039;&#039; — Toggle (installer default &#039;&#039;&#039;true&#039;&#039;&#039;).&lt;br /&gt;
* &#039;&#039;&#039;Require signed SAML assertions&#039;&#039;&#039; — Toggle (recommended &#039;&#039;&#039;true&#039;&#039;&#039; for production).&lt;br /&gt;
&lt;br /&gt;
===== Identity Provider =====&lt;br /&gt;
&lt;br /&gt;
Choose the IdP type: Microsoft Entra ID (Azure AD), AD FS (on-premises), Okta, Auth0, or Generic SAML 2.0 (covers Keycloak &#039;&#039;/protocol/saml/descriptor&#039;&#039;, Ping, Google Workspace, and similar). Follow-on screens seed example metadata URLs and default attribute names.&lt;br /&gt;
&lt;br /&gt;
Provide &#039;&#039;&#039;either&#039;&#039;&#039; an HTTPS federation metadata URL &#039;&#039;&#039;or&#039;&#039;&#039; a local federation XML absolute path — choose exactly one. Later panels cover attribute options (&#039;&#039;friendlyName&#039;&#039;, &#039;&#039;valuePath&#039;&#039;, &#039;&#039;stripPrefix&#039;&#039;, &#039;&#039;attribute.user&#039;&#039;, &#039;&#039;attribute.displayName&#039;&#039;, &#039;&#039;ForceAuthn&#039;&#039;, &#039;&#039;RequestedAuthnContext&#039;&#039;, NameID format).&lt;br /&gt;
&lt;br /&gt;
===== Group → Role Mapping =====&lt;br /&gt;
&lt;br /&gt;
The closing panels map IdP group attribute values to Obsidian roles, including &#039;&#039;&#039;Require role assignment&#039;&#039;&#039; (default yes) and optional &#039;&#039;dynamicRole.prefix&#039;&#039; / &#039;&#039;suffix&#039;&#039;. At least one mapping to the &#039;&#039;&#039;Admin&#039;&#039;&#039; role is required. See [[Advanced_Configuration#SAML_Authentication_Properties|SAML Authentication Properties]] for the full property reference.&lt;br /&gt;
&lt;br /&gt;
===== SP metadata artefacts =====&lt;br /&gt;
&lt;br /&gt;
During install the tooling writes &#039;&#039;&#039;sp-metadata.xml&#039;&#039;&#039; under the Obsidian deployment directory; the same metadata is available live at &#039;&#039;&#039;/saml/metadata&#039;&#039;&#039; after startup. Register whichever artefact your IdP admin prefers.&lt;br /&gt;
&lt;br /&gt;
===== Before you complete install =====&lt;br /&gt;
&lt;br /&gt;
* Ensure ACS and Entity ID &#039;&#039;&#039;https&#039;&#039;&#039; URLs are reachable from IdPs (align with reverse proxies).&lt;br /&gt;
* For Entra &#039;&#039;&#039;150+&#039;&#039;&#039; group memberships in SAML assertions, expect attribute suppression — filter groups or map assigned app roles instead. (OIDC uses a &#039;&#039;&#039;200&#039;&#039;&#039; group threshold; the numbers differ by protocol.)&lt;br /&gt;
&lt;br /&gt;
==== LDAP Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.LDAP.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve selected LDAP Authentication, this screen takes you through configuring the server address and the various elements used to grant access. You should familiarize yourself with Obsidian [[Authenticator#Roles|Roles]]. The &#039;&#039;Access DN&#039;&#039; configuration element grants Read access to Obsidian application. You may use the same DN for more than one Role should you so wish.&lt;br /&gt;
&lt;br /&gt;
==== Custom Authenticator Configuration====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.CustomAuthenticator.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have chosen to use your own authentication mechanism, you must enter the fully qualified classname here.&lt;br /&gt;
&lt;br /&gt;
==== Database Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Database.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Next comes database configuration. If you are using JNDI, leave the username and password fields blank. If using JDBC URL, username and password fields are required.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;Database connections per instance&#039;&#039; and &#039;&#039;Database connection timeout(millis)&#039;&#039; fields are required and provide directive to the connection pool.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Database table name prefix&#039;&#039; is optional and is typically used when Obsidian will be colocated in an existing database/schema.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Database schema (Oracle/PostgreSQL)&#039;&#039; - This is used to allow for an alternate schema other than the default user&#039;s schema. We also recommend setting this value with Oracle/PostgreSQL when using JNDI as it allows more efficient database metadata loading.&lt;br /&gt;
&lt;br /&gt;
By default, the Obsidian installation will only include the JDBC libraries necessary for your particular database. At times you may wish to include the others to be able to change between databases. In those cases, check &#039;&#039;Include all supported JDBC libs&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Email Configuration ====&lt;br /&gt;
&lt;br /&gt;
If not using JNDI for mail sessions, provide the server and authentication details as required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.SSL.Email.png]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.TLS.Email.png]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.Open.Email.png]]&lt;br /&gt;
&lt;br /&gt;
==== JNDI Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.JNDI.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have selected JNDI for mail sessions, provide the JNDI path here. If you are using JNDI for Database connections, specify the database type.&lt;br /&gt;
&lt;br /&gt;
==== Configuring 3rd Party Library Conflict Management ====&lt;br /&gt;
[[Image:JarJar-Obsidian-6.0.0.png]]&lt;br /&gt;
&lt;br /&gt;
The Obsidian installer allows you to use [https://code.google.com/archive/p/jarjar JarJar] to handle potential conflicts between Obsidian&#039;s use of 3rd party libraries and versions used within your application. On this screen, simply select which libraries to which you wish to apply the JarJar bytecode modification process. This is optional and is skipped if no libraries are selected. Any jars not listed for which you are using more recent versions can be used and are therefore not included as options in the JarJar processing.&lt;br /&gt;
&lt;br /&gt;
==== Selecting Script Libraries ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-6.0.0-Choose-Scripting-Libraries.png]]&lt;br /&gt;
&lt;br /&gt;
The Obsidian installer allows you to select which script libraries you wish to include. Jython and JRuby are incompatible with each other, so the installer will only allow one of the two selected. If all script libraries are deselected, the only script jobs that can be run in Obsidian will be Javascript jobs as that engine is built into Java.&lt;br /&gt;
&lt;br /&gt;
==== Choosing Email Support ====&lt;br /&gt;
As of Obsidian 5.2.0, you can choose between JavaMail (javax) and Jakarta mail (Jakarta EE) implementations. When choosing Jakarta, you can also choose to bundle the Angus Jakarta compatible implementation. &lt;br /&gt;
&lt;br /&gt;
If you are using JNDI, this will only include the relevant support in the Obsidian WAR artifacts and the actual libraries in use must be provided by you and bundled with your container. If the standalone scheduler is selected along with JNDI, JNDI is assumed only relevant for WARs. As such, default libraries for either JavaMail or Jakarta will be included in the standalone installation.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.EmailImplementation.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
==== Additional configuration items ====&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 5.5.0, if you require any additional configuration items such as additional appenders/loggers or event hook configurations, you can add them here.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.5.0.Extra.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
==== Offline or restricted-network install ====&lt;br /&gt;
&lt;br /&gt;
Obsidian 7.0.0 resolves third-party libraries when you run the installer. For environments without direct access to Maven Central:&lt;br /&gt;
&lt;br /&gt;
# Pre-stage a local dependency cache containing every coordinate listed in the Obsidian dependency inventory for your release (contact Carfey support or your account team for the checklist file matching &#039;&#039;&#039;Obsidian-Install-7.0.0.jar&#039;&#039;&#039;).&lt;br /&gt;
# Copy the cache to the install host. Two layouts are supported:&lt;br /&gt;
#* &#039;&#039;&#039;Flat:&#039;&#039;&#039; one file per installed JAR name directly under the cache root.&lt;br /&gt;
#* &#039;&#039;&#039;File Maven repo:&#039;&#039;&#039; &#039;&#039;&#039;&amp;lt;group&amp;gt;/&amp;lt;artifact&amp;gt;/&amp;lt;version&amp;gt;/&amp;lt;installed-filename&amp;gt;.jar&#039;&#039;&#039;&lt;br /&gt;
# Run the installer with offline mode and the cache directory:&lt;br /&gt;
&lt;br /&gt;
 java -Dobsidian.install.deps.offline=true -Dobsidian.install.deps.cache.dir=/path/to/cache -jar Obsidian-Install-7.0.0.jar&lt;br /&gt;
&lt;br /&gt;
If a required library is missing from the cache, the install stops with an error naming the coordinate — add that artifact to the cache and re-run.&lt;br /&gt;
&lt;br /&gt;
===== Corporate Maven mirror (online) =====&lt;br /&gt;
&lt;br /&gt;
When your site mirrors Maven Central, point the installer at your repository base URL:&lt;br /&gt;
&lt;br /&gt;
 java -Dobsidian.install.deps.repo.url=https://nexus.example.com/repository/maven-public/ -jar Obsidian-Install-7.0.0.jar&lt;br /&gt;
&lt;br /&gt;
The installer reuses resolved artifacts for the duration of one install session.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Completing the Installation ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Finished.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Once you&#039;ve completed the Installation and Configuration screens, fully configured Obsidian Scheduler artifacts are now ready for you to use in the installation path you selected.&lt;br /&gt;
&lt;br /&gt;
Should you wish to automate future installations with the same configuration, click &#039;&#039;Generate an automatic installation script&#039;&#039;. This will prompt you to save an XML file that can be used for future installations using the automated install procedure &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar my-obsidian-configuration.xml&amp;lt;/code&amp;gt;. Every effort is made to ensure compatibility of these automated install files between versions. Any incompatibility will be noted in the [[Release_Notes|Release Notes]]. These automated installer files can also be used as templates for other environments, modifying them as necessary.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Main_Page&amp;diff=4170</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Main_Page&amp;diff=4170"/>
		<updated>2026-08-02T02:24:52Z</updated>

		<summary type="html">&lt;p&gt;Craig: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;[[Obsidian]]&#039;&#039;&#039; is a fully-featured Java-based scheduling application which supports load balancing, failover and job workflow. It is designed for [[Deployment Diagram|high availability]], and it is specially built for demanding scheduling environments.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;[[Release_Notes#Obsidian_7.0.1|Version 7.0.1]]&#039;&#039;&#039; is the latest release, and was released in August 2026.&lt;br /&gt;
&lt;br /&gt;
Check out &#039;&#039;&#039;[[Getting Started]]&#039;&#039;&#039; if you are looking to set up Obsidian.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;[[User Guide]]&#039;&#039;&#039; will provide all the information you need to use all of Obsidian&#039;s functionality, and provides a quick view of the features Obsidian supports.&lt;br /&gt;
&lt;br /&gt;
Our &#039;&#039;&#039;[[FAQ]]&#039;&#039;&#039; is also worth checking out if you have questions about Obsidian or want more context about what it does.&lt;br /&gt;
&lt;br /&gt;
== Full Administration Capabilities ==&lt;br /&gt;
&lt;br /&gt;
Obsidian features a rich administration web interface, allowing easy monitoring and management. See the &#039;&#039;&#039;[[Admin Web Application Guide]]&#039;&#039;&#039; for details.&lt;br /&gt;
&lt;br /&gt;
[[Image:ObsidianNav 4.0.png]]&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Release_Notes&amp;diff=4169</id>
		<title>Release Notes</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Release_Notes&amp;diff=4169"/>
		<updated>2026-08-02T02:24:21Z</updated>

		<summary type="html">&lt;p&gt;Craig: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Please review our [[Upgrading_Obsidian|Upgrade Instructions]].&lt;br /&gt;
&lt;br /&gt;
Read about our [[Planned_Releases|Planned Releases]].&lt;br /&gt;
&lt;br /&gt;
Looking for old release notes? See [[Release_Notes_-_Older_Releases|Release Notes - Older Releases]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;toclimit-2&amp;quot;&amp;gt;__TOC__&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Obsidian 7.0.1 ==&lt;br /&gt;
&lt;br /&gt;
Released August 2026&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Fixed site and hardware-linked licence verification on Obsidian 7.0.0. Workaround for 7.0.0 available on request.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 7.0.0 ==&lt;br /&gt;
&lt;br /&gt;
Released July 2026&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;OAuth 2.0 / OIDC Single Sign-On&#039;&#039;&#039; — Obsidian now supports OAuth 2.0 / OIDC SSO authentication via [[Authenticator#OAuthAuthenticator_(OIDC_SSO)|OAuthAuthenticator]]. Configure any OIDC-compliant identity provider (Keycloak, Microsoft Entra ID, Okta, Auth0, or Generic OIDC) as the login provider. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties|OAuth/OIDC Authentication Properties]] for full configuration details.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Bearer JWT authentication for the REST API&#039;&#039;&#039; — REST clients can now authenticate using a signed JWT access token in the &#039;&#039;Authorization: Bearer&#039;&#039; header. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties|OAuth/OIDC Authentication Properties]] for configuration.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Embedded Tomcat quick start&#039;&#039;&#039; — Evaluation and local runs use an Apache Tomcat 10.1.x embedded runtime (Jakarta Servlet). The bundled sample is &#039;&#039;&#039;h2-tomcat-quick-start.xml&#039;&#039;&#039;. &#039;&#039;webObsidian&#039;&#039; scripts start/stop embedded Tomcat for scheduler or admin-only profiles — see [[Installation_Guide#Deployment_options|Deployment options]] and [[Getting_Started#Starting_Obsidian_with_embedded_Tomcat|Starting Obsidian with embedded Tomcat]].&lt;br /&gt;
** On Linux, embedded Tomcat packs also install optional user-level systemd and desktop integration — see [[Installation_Guide#Linux_desktop_and_systemd_integration|the installer option]] and [[Getting_Started#Running_Obsidian_as_a_Linux_user_service|Running Obsidian as a Linux user service]].&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Global parameter autocomplete and tooltips&#039;&#039;&#039; — While editing a job, typing &#039;&#039;{{&#039;&#039; in a parameter value opens suggestions for global parameter names (subject to visibility settings). On job view, hover over values containing &#039;&#039;Name&#039;&#039; to see resolved global values when your user&#039;s permissions allow. Administration screens for defining global parameters are unchanged. See [[Advanced_Configuration|Advanced Configuration]] for visibility settings.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Install-time dependency resolution&#039;&#039;&#039; — The published installer download does not embed public open-source JARs. Libraries are fetched (or read from a pre-staged cache) when you run the installer. See [[Installation_Guide#Offline_or_restricted-network_install|Offline install]] for air-gapped preparation steps.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Installer upgrade (IzPack 5)&#039;&#039;&#039; — Obsidian 7.0.0 ships an IzPack 5-based installer. GUI and headless installs behave as in Obsidian 6 for standard flows. Customers with saved Obsidian 6 automated install XML must run the migrate helper included in the download before headless install. See [[Upgrading_Obsidian#Migrating_saved_automated_install_XML_.28Obsidian_6_.E2.86.92_7.29|Upgrading Obsidian]].&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Modular Groovy and Flexmark libraries&#039;&#039;&#039; — Obsidian no longer ships &#039;&#039;&#039;apache-groovy-all-4.0.24.jar&#039;&#039;&#039; or &#039;&#039;&#039;flexmark-util-0.62.2.jar&#039;&#039;&#039; uber archives. Scripting and UI documentation rendering use the modular JAR sets documented in [[Advanced_Configuration#Dependent_Libraries|Dependent Libraries]].&lt;br /&gt;
&lt;br /&gt;
=== Removals ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Remember Me login option removed&#039;&#039;&#039; — The Remember Me checkbox and associated session-cookie mechanism have been removed. The &#039;&#039;USER_COOKIE&#039;&#039; table and &#039;&#039;allowRememberMe&#039;&#039; system parameter are removed automatically during the upgrade to 7.0.0; no operator action is required.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;User logout&#039;&#039;&#039; - Fixed an issue where users were not fully logged out in certain session states.&lt;br /&gt;
* &#039;&#039;&#039;Job edit screen accessible to users with no roles&#039;&#039;&#039; — Users with no assigned roles could previously access the job edit screen but not make changes; this has been fixed.&lt;br /&gt;
* &#039;&#039;&#039;Firefox UI icons&#039;&#039;&#039; — Fixed icons not rendering correctly in Firefox.&lt;br /&gt;
* Jakarta emails sent with both text and HTML would drop HTML portion; this has been fixed.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.4.0 ==&lt;br /&gt;
Released December 2025&lt;br /&gt;
&lt;br /&gt;
=== Enhancements === &lt;br /&gt;
* New [[Built-in_Jobs#Obsidian_Execution_Statistics_Job|Execution Statistics Job]] and built in [[Admin_Job_Stats|UI]] and [[Embedded_API#List_Stats|Embedded API]] and [[REST_Endpoints#GET_a_list_of_job_execution_statistics|REST API]]&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Fixed issue preventing [[Admin_Jobs#Deleting|job deletion]] when resubmissions of failed executions existed. Bug existed in UI and APIs.&lt;br /&gt;
* Fixed UI styling issue for [[Admin_Jobs#Deleting|job deletion]] where checkbox label did not appear.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.3.3 ==&lt;br /&gt;
Released November 2025&lt;br /&gt;
&lt;br /&gt;
=== Enhancements === &lt;br /&gt;
* [https://web.obsidianscheduler.com/obsidianapi/com/carfey/suite/security/LdapAuthenticator.html LDAPAuthenticator] supports configurable check user active override - com.carfey.suite.security.LdapAuthenticator.checkActiveAttribute&lt;br /&gt;
* [https://web.obsidianscheduler.com/obsidianapi/com/carfey/suite/security/LdapAuthenticator.html#isUserActive(javax.naming.directory.DirContext,java.lang.String) LDAPAuthenticator.isUserActive(DirContext,String)] visibility increased to protected to allow for functionality override in subclasses.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Correct LdapAuthenticator case with delimited configuration where active check wasn&#039;t applied.&lt;br /&gt;
* [https://web.obsidianscheduler.com/obsidianapi/com/carfey/suite/security/LdapAuthenticator.html#isUserActive(javax.naming.directory.DirContext,java.lang.String) LDAPAuthenticator.isUserActive(DirContext,String)] javadoc added for isUserActive fully detailing the implementation.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.3.2 ==&lt;br /&gt;
Released October 2025&lt;br /&gt;
&lt;br /&gt;
=== Enhancements === &lt;br /&gt;
* Notification failures now trigger Dispatch category Error level event on first failure. For use in log alerts and Event Hooks.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Notification failures no longer log on every failure reducing chatty failure logs.&lt;br /&gt;
* No-arg constructors added to multiple JSON serializable candidate POJOs missing them. Addresses GSON&#039;s workaround use of sun.misc.Unsafe.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.3.1 ==&lt;br /&gt;
Released August 2025&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Fix failure response codes on [[REST_Endpoints#GET_health_details_on_an_existing_scheduling_host|REST health endpoint]]&lt;br /&gt;
* Fix automatic upgrade issue from 6.1.0 to any of 6.2.0, 6.2.1, 6.3.0&lt;br /&gt;
* Fix auth issue when [[Admin_User_Management#Multi-Factor_Authentication_.28MFA.29|MFA]] is enabled that allows REST access when account has been locked out due to MFA not being setup.  [[Contact_the_Obsidian_Scheduler_Team|Contact us]] if you need workaround details for earlier versions.&lt;br /&gt;
* Fix native auth issue that allows REST access when account has been flagged as inactive.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.3.0 ==&lt;br /&gt;
Released July 2025&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* Cron day of month expansion for [[Cron#Special_Character_Usage|day of week proximity]] using [[Cron#Examples|~]]&lt;br /&gt;
* Annotation based job and chain configuration [[Initializing_and_Restoring#Annotation_Initialization|initialization]]&lt;br /&gt;
* Health endpoint with details for Job Queuer and Job Spawner - [[REST_Endpoints#GET_health_details_on_an_existing_scheduling_host|REST API]] or Embedded API [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/HostManager.html#getHealth(java.lang.String) by hostname] or [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/HostManager.html#getHealth(long) by host id]&lt;br /&gt;
* JDBC URL construction from parts [[Advanced_Configuration#Database_Properties|host/port/databaseName/dbType/oracleSid]]&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.2.1 ==&lt;br /&gt;
Released June 2025&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Certain recoveries greater than 24 hours using new day of month proximity patterns introduced in 6.2.0 would result in no jobs spawning.&lt;br /&gt;
* Migrations targeting 6.0.0 and greater that start earlier than 5.0.0 skip the 5.0.0 migration. Migrating to any 5.x version first and then to 6.0.0 or later works around this issue.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.2.0 ==&lt;br /&gt;
Released May 2025&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* Cron day of month expansion for [[Cron#Special_Character_Usage|day of week proximity]] using [[Cron#Examples|&amp;lt; &amp;gt; ≥ ≤]]&lt;br /&gt;
* Cron day of month expansion for [[Cron#Special_Character_Usage|weekday proximity]] using [[Cron#Examples|&amp;lt; &amp;gt; ≥ ≤]]&lt;br /&gt;
* [[Admin_Global_Parameters#Using_Global_Parameters_in_Jobs|Global Parameters]] reference in job edit page support mouseover/title display of actual value when permissions allow&lt;br /&gt;
* Host time displayed in [[Admin_Host_Status|hosts status]] in UI&lt;br /&gt;
* [[Admin_Job_Runtime_Preview|Runtime Preview]] optimizations for large date ranges in both UI and APIs.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Combination of table prefix, sorting by clob column and Oracle metadata load failure no longer results in sql error. This occurred in the UI when viewing raw job history results and in API calls for job runtime results.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.1.1 ==&lt;br /&gt;
Released March 2025&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* [[Authenticator#Implementation_of_a_Custom_Authenticator|Authenticator]] supports optional &amp;lt;code&amp;gt;authenticateREST()&amp;lt;/code&amp;gt; method.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Native authentication REST logins do not apply &#039;last login&#039; timestamps. Avoids noisy error that was appearing in logs.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.1.0 ==&lt;br /&gt;
Released March 2025&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* Native authentication now disables users after 4 invalid login attempts, either password or MFA code if applicable. Invalid attempts threshold is configurable.&lt;br /&gt;
* Native authentication last login datetime displayed on list user screen.&lt;br /&gt;
* Stronger licensing controls. Site and hardware licenses as of this version must be regenerated by Carfey Software and every 2 years. Contact us at licensing [[Image:atSymbol.png]] obsidianscheduler.com.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Native authentication password complexity pattern properly created and no longer overwritten on restart.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.0.1 ==&lt;br /&gt;
Released February 2025&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* [[Built-in_Jobs#File_Archive_Job|File Archive Job]] and [[Built-in_Jobs#File_Scanner_Job|File Scanner Job]] both now support min/max file sizes and minimum age.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Native authentication user updates now support long passwords.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 6.0.0 ==&lt;br /&gt;
Released December 27, 2024&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* Jakarta Servlet 5.0&lt;br /&gt;
** Servlet container for Web Admin must support Jakarta Servlet 5.0 (e.g. Tomcat 10, Jetty 11)&lt;br /&gt;
* [[Micronaut_Integration|Micronaut]] integration support&lt;br /&gt;
* [[Advanced_Configuration#Dependent_Libraries|Groovy 4]] support&lt;br /&gt;
* [[Advanced_Configuration#Properties.2FYaml_File|Yaml]] configuration support&amp;lt;ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using automated installer files from previous releases, you will need to add an additional configuration item to choose between yaml and properties formats in UserInputPanel.0.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;com.izforge.izpack.panels.UserInputPanel id=&amp;quot;UserInputPanel.0&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;userInput&amp;gt;&lt;br /&gt;
......snip......&lt;br /&gt;
            &amp;lt;entry key=&amp;quot;config.format&amp;quot; value=&amp;quot;yaml&amp;quot; /&amp;gt;&lt;br /&gt;
OR&lt;br /&gt;
            &amp;lt;entry key=&amp;quot;config.format&amp;quot; value=&amp;quot;properties&amp;quot; /&amp;gt;&lt;br /&gt;
......snip......&lt;br /&gt;
        &amp;lt;/userInput&amp;gt;&lt;br /&gt;
    &amp;lt;/com.izforge.izpack.panels.UserInputPanel&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/ref&amp;gt;&lt;br /&gt;
* Native authentication supports customizable [[Admin_Login#Password_Complexity|password complexity]] requirements&lt;br /&gt;
* XML support deprecated across the product including XML UI downloads, XML runner configurations and XML license leases.&lt;br /&gt;
** &#039;&#039;&#039;Starting January 1st 2027, XML license lease requests will stop being processed. All Obsidian instances running using internet-verified licenses (including licence key proxies) will be required to use release 6.0.0 or later as of January 1st 2027.&#039;&#039;&#039;&lt;br /&gt;
** Above noted XML support will be removed in the first Obsidian version released in 2027.&lt;br /&gt;
* License leases use JSON payloads.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.5.1 ==&lt;br /&gt;
Released December 16, 2024&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Admin only (no scheduler) UI no longer generates event hook errors while running nor during shutdown&lt;br /&gt;
* Quick start installer file no longer generates errors during installation&lt;br /&gt;
* A few small web UI enhancements&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.5.0 ==&lt;br /&gt;
Released October 2024&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* Event Hooks management available in [[Admin_Host_Status#Event_Hook_Status|UI]], [[Embedded_API#Event_Hook_Resume_or_Pause|Embedded API]] and [[REST_Endpoints#POST_event_hook_pause_or_resume|REST API]].&lt;br /&gt;
* [[Installation_Guide#Additional_configuration_items|Installer]] supports custom add on configurations    &#039;&#039;&#039;Potential breaking change to automated installer files.&#039;&#039;&#039; &amp;lt;ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using automated installer files from previous releases, you will need to add a new section of xml as of Obsidian 5.5.0 to handle a new UserInputPanel. Immediately after the UserInputPanel.17 closing brace, add the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;com.izforge.izpack.panels.UserInputPanel id=&amp;quot;UserInputPanel.18&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;userInput&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.1&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.2&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.3&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.4&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.5&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.6&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.7&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.8&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.9&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.10&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.11&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.12&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.13&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.14&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.15&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.16&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.17&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.18&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.19&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.log4j2.properties.20&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.1&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.2&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.3&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.4&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.5&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.6&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.7&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.8&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.9&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.11&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.10&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.12&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.13&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.14&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.15&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.16&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.17&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.18&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.19&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
		&amp;lt;entry key=&amp;quot;extra.carfey.properties.20&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
	&amp;lt;/userInput&amp;gt;&lt;br /&gt;
&amp;lt;/com.izforge.izpack.panels.UserInputPanel&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Certain Cron expressions that fail to generate text descriptions no longer impact scheduling.&lt;br /&gt;
* Text database columns were previously restricted to maximum length of MySQL implementation. Corrected to validate length via DB implementation.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.4.0 ==&lt;br /&gt;
Released June 2024&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* Event Hooks available in [[Embedded_API#List_Event_Hooks|Embedded API]] and [[REST_Endpoints#GET_event_hooks|REST API]].&lt;br /&gt;
* [[Advanced_Configuration#Dependent_Libraries|GSON library]] upgrade to support Java 21&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Oracle identifier no longer too long when using prefixes.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.3.0 ==&lt;br /&gt;
Released March 2024&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* New [[Admin_Host_Status#Event_Hook_Status| Event Hooks Status]] available in the UI.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Some improvements throughout the [[Admin_Web_Application_Guide|Admin Web Application]] for autofocus of fields.&lt;br /&gt;
* Additional classes and interfaces added to [https://web.obsidianscheduler.com/obsidianapi/ javadoc].&lt;br /&gt;
* Some cleanup in [https://web.obsidianscheduler.com/obsidianapi/ javadoc] documenation.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.2.1 == &lt;br /&gt;
Released January 2024&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* [[Built-in_Jobs#Maintenance_Jobs|Maintenance Jobs]] are now scheduled by default in new installations. Can be disabled via [[Advanced_Configuration#Miscellaneous_Properties|Configuration]] property.&lt;br /&gt;
* New [[Event_Hooks#Standard_Output.2FError_Streams_Event_Hook | Standard Output/Error Streams Event Hook]].&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Schedule descriptions are now updated after edits are applied in all UI screens and APIs.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.2.0 ==&lt;br /&gt;
Released December 2023&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* [[Cron]] &amp;amp; [[Cron#Recurrence|Recur]] patterns along with any use of [[Admin_Schedule_Aliases|Schedule Aliases]] now support plain language description of the patterns throughout the [[Admin_Web_Application_Guide|UI]], visible while hovering patterns, and in [[REST_Endpoints|REST]] and [[Embedded_API|Embedded]] API responses.&lt;br /&gt;
* All screens supporting UI exports now support JSON ([[Admin_Job_Activity#Exporting_Results|Job Activity]], [[Admin_Jobs#Exporting_Results|Jobs]], [[Admin_Job_Runtime_Preview#Exporting_Results|Runtime Previews]], [[Admin_Job_Chains#Job_Chain_Listing|Job Chains]], [[Admin_Logs#Exporting_Results|Logs]], [[Admin_Notifications#Exporting_Results|Sent Notifications]], [[Admin_User_Management#Exporting_Results|Users]], [[Admin_Custom_Calendars#Calendar_Listing|Calendars]])&lt;br /&gt;
* All screens supporting UI exports and search criteria and/or inline filters now include any specified search criteria and filter text in Excel, XML and JSON downloads ([[Admin_Job_Activity#Exporting_Results|Job Activity]], [[Admin_Jobs#Exporting_Results|Jobs]], [[Admin_Job_Runtime_Preview#Exporting_Results|Runtime Previews]], [[Admin_Job_Chains#Job_Chain_Listing|Job Chains]], [[Admin_Logs#Exporting_Results|Logs]], [[Admin_Notifications#Exporting_Results|Sent Notifications]], [[Admin_User_Management#Exporting_Results|Users]])&lt;br /&gt;
* Support for [[Installation_Guide#Choosing_Email_Support|Jakarta EE mail]] &#039;&#039;&#039;Potential breaking change to automated installer files.&#039;&#039;&#039; &amp;lt;ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using automated installer files from previous releases and had email configured, you will need to add a new section of xml as of Obsidian 5.2.0 to handle a new UserInputPanel. Immediately after the UserInputPanel.16 closing brace, add the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;com.izforge.izpack.panels.UserInputPanel id=&amp;quot;UserInputPanel.17&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;userInput&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;mail.type.selection&amp;quot; value=&amp;quot;javax&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;/userInput&amp;gt;&lt;br /&gt;
&amp;lt;/com.izforge.izpack.panels.UserInputPanel&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/ref&amp;gt;&lt;br /&gt;
* New [[Event_Hooks#REST_Endpoint_Event_Hook|REST Endpoint Event Hook]]&lt;br /&gt;
* [[Key_Server_Proxy|Key Server proxy]] artifact obtained via web download during installation&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* Time picker buttons (hour, minute, AM/PM) in [[Admin_Job_Activity#Filtering|Job Activity filtering]] no longer change other elements of the selected time.&lt;br /&gt;
* Cron pattern with [[Cron#Special_Character_Usage|LW]] and any other non-L value in day position no longer also incorrectly evaluates to last day.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.1.1 ==&lt;br /&gt;
Released June 2023&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
&lt;br /&gt;
* [[Authenticator|Native authentication]] no longer fails when user deletes are attempted from the UI.&lt;br /&gt;
* Built in maintenance job [[Built-in_Jobs#Job_History_Cleanup_Job|Job History Cleanup]] no longer leaves deletion candidate CHAIN SKIPPED records in the JOB_HISTORY table in rare circumstances.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.1.0 ==&lt;br /&gt;
Released April 2023&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
&lt;br /&gt;
* [[Admin_Schedule_Aliases#Schedule_Alias_Fragments|Schedule Aliases]] now support fragments for configuration-time substitutions.&lt;br /&gt;
* New convenience job [[Built-in_Jobs#Database_File_Export_Job|Database File Export Job]] for generating basic file extracts from database queries.&lt;br /&gt;
* New convenience job [[Built-in_Jobs#REST_Invocation_Job|REST Invocation Job]] for making simple REST calls and storing results.&lt;br /&gt;
* Performance improvements in job failure handling.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.0.4 == &lt;br /&gt;
Released February 2022&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Log4j2 2.17.1 as fix for [https://logging.apache.org/log4j/2.x/security.html#log4j-2.17.1 RCE vulnerability] where attackers can modify log4j configuration.&lt;br /&gt;
* Restore missing default log4j2 configuration in installation artifacts.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.0.3 == &lt;br /&gt;
Released December 2021&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Log4j2 2.17.0 as fix for [https://logging.apache.org/log4j/2.x/security.html#log4j-2.17.0 DOS vulnerability]&lt;br /&gt;
* Fix native login issue showing as inactive on some databases.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.0.2 == &lt;br /&gt;
Released December 2021&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Log4j2 2.16.0 as permanent fix for [https://logging.apache.org/log4j/2.x/security.html#log4j-2.16.0 RCE vulnerability]&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.0.1 == &lt;br /&gt;
Released December 2021&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Log4j2 2.15.0 as fix for [https://logging.apache.org/log4j/2.x/security.html#log4j-2.15.0 RCE vulnerability]&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
* Fix sporadic native login issue on some databases.&lt;br /&gt;
* Formatting fix in quick installer file&lt;br /&gt;
&lt;br /&gt;
== Obsidian 5.0.0 == &lt;br /&gt;
Released August 2021.&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Java 11 (minimum Java version)&lt;br /&gt;
* [[Admin_Schedule_Aliases|Schedule Aliases]] including support in [[REST_Endpoints#Schedule_Alias_Endpoints|REST API]] and [[Embedded_API#ScheduleAliasManager_API|Embedded API]]&lt;br /&gt;
* [[Admin_User_Management#Multi-Factor_Authentication_.28MFA.29|MFA Support]] for UI logins&lt;br /&gt;
* New [[Admin_User_Management#User_Rights|Author and Operator]] roles&lt;br /&gt;
* Convention-based role permissions by [[Admin_User_Management#Job_Folder_Rights|root job folder]] for Write, Author and Operator.&amp;lt;ref&amp;gt;&lt;br /&gt;
There is a possibility of a breaking change to Embedded or REST API use due to the need to change the [[Embedded_API#Enumerations|User Role enumeration]] from a Java enum to an enum-style class to support this feature. Bringing in the upgraded Obsidian library and compiling should reveal any such broken use of these enumerations. Needed changes should be minor and self-explanatory.&amp;lt;/ref&amp;gt;&lt;br /&gt;
* Bundled Jetty 10.0.2&lt;br /&gt;
* Legacy Embedded API (from Obsidian 1.5) dropped&lt;br /&gt;
* Signal handler disabled by default. Enabled only via [[Advanced_Configuration#Miscellaneous_Properties|configuration]].&lt;br /&gt;
* Many [[Advanced_Configuration#Dependent_Libraries|library upgrades]].&lt;br /&gt;
* UI javascript library updates.&lt;br /&gt;
&lt;br /&gt;
== Footnotes ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Advanced_Configuration&amp;diff=4168</id>
		<title>Advanced Configuration</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Advanced_Configuration&amp;diff=4168"/>
		<updated>2026-07-31T17:15:44Z</updated>

		<summary type="html">&lt;p&gt;Craig: /* Dependent Libraries */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian requires some initial configuration parameters for database connection information and desired authentication mechanisms.  The installer takes care of configuring these as provided.  For your reference should you wish to manually create/edit these, note the details below.&lt;br /&gt;
&lt;br /&gt;
== Configuration Details ==&lt;br /&gt;
&lt;br /&gt;
The following sections show the available properties and sample values that can be used in the &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; properties file. While properties format is shown, all the same configuration items apply to yaml support. See [[#Properties.2FYaml_File|Properties/Yaml File]] for more details on this file.&lt;br /&gt;
&lt;br /&gt;
=== Authentication Properties ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Comment out the native authenticator setup&lt;br /&gt;
#com.carfey.suite.security.Authenticator=com.carfey.suite.security.DBAuthenticator&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LDAP Authentication Properties ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#Set your LDAP info here&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.LdapAuthenticator&lt;br /&gt;
# You can specify the attribute type used in building up the distinguished name (dn). If unspecified, defaults to cn&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.dn.attribute=uid&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of dn.bases to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.dn.base=ou=people,o=MyOrgHere&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.url=ldap://localhost:10389&lt;br /&gt;
# You can specify the SECURITY_AUTHENTICATION. Defaults to simple.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.securityAuthentication=simple&lt;br /&gt;
# Any necessary additional information such as Provider, Principal and Host will need to be handled in your custom class extending LdapAuthenticator.&lt;br /&gt;
&lt;br /&gt;
# You can login via an LDAP attribute that is not part of the dn. A matching entry is found in the LDAP Directory (using anonymous or a fixed lookup account)&lt;br /&gt;
# after which the dn attribute above is retrieved to perform the user&#039;s authentication. The following 5 attributes are only used for this type of lookup and authentication.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.lookupDnAttribute=false&lt;br /&gt;
# This is the attribute name that is being searched for in the LDAP directory to build the dn for eventual authentication.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.loginAttribute=sAMAccountName&lt;br /&gt;
# This determines if these lookups will be done anonymously (without a session authenticated by user/password)&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.anonymousEnabled=false&lt;br /&gt;
# If anonymous lookups are not permitted or desired, provide the dn and password to be used for lookups. Typically, a read-only account with&lt;br /&gt;
# read rights to the LDAP entries and attributes in question is sufficient.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchUserFullDn=cn=ObsidianLDAPReadOnly,ou=people,o=MyOrgHere&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchPassword=password&lt;br /&gt;
# As of Obsidian 4.5.1, an alternate search base configuration parameter is used for the searchUser&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchUserDnBase=ou=people,o=MyOrgHere&lt;br /&gt;
# As of Obsidian 4.7.1, you may specify one or more additional group membership attributes beyond the defaults&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.groupMembershipAttribute=businessGroup~~~departmentName&lt;br /&gt;
&lt;br /&gt;
# Configure who may access the web app, based on LDAP group membership&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.accessDN=cn=SchedulerAccess,ou=groups,o=MyOrgHere&lt;br /&gt;
&lt;br /&gt;
# Configure the Write role used in the admin web app, based on LDAP group membership (by default, users may only read)&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.write.dn=cn=SchedulerWrite,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.write.roleName=Write&lt;br /&gt;
&lt;br /&gt;
# Configure the Admin role used in the admin web app, based on LDAP group membership (users may configure system parameters, etc).&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.admin.dn=cn=SchedulerAdmin,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.admin.roleName=Admin&lt;br /&gt;
&lt;br /&gt;
# Configure the LimitedRead role used in the admin web app, based on LDAP group membership (by default, users may only read)&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.limitedRead.dn=cn=SchedulerLimitedRead,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.limitedRead.roleName=LimitedRead&lt;br /&gt;
&lt;br /&gt;
# Configure the APIrole used by the REST API, based on LDAP group membership&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.api.dn=cn=SchedulerAPI,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.api.roleName=API&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OAuth / OIDC Authentication Properties ====&lt;br /&gt;
&lt;br /&gt;
To use OAuth / OIDC SSO, set the &#039;&#039;Authenticator&#039;&#039; property to&lt;br /&gt;
&#039;&#039;com.carfey.suite.security.OAuthAuthenticator&#039;&#039; and add the properties below. All&lt;br /&gt;
properties are prefixed with &#039;&#039;com.carfey.suite.security.OAuthAuthenticator.&#039;&#039; (shown&lt;br /&gt;
without prefix in the table for brevity).&lt;br /&gt;
&lt;br /&gt;
===== Required properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;issuer&#039;&#039; || OIDC Issuer URL. Obsidian fetches &#039;&#039;&amp;lt;issuer&amp;gt;/.well-known/openid-configuration&#039;&#039; lazily on first use and caches it.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clientId&#039;&#039; || Client ID registered at the IdP.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clientSecret&#039;&#039; || Client secret. Keep out of version control.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;redirectUri&#039;&#039; || Exact callback URL registered at the IdP (e.g. &#039;&#039;https://app.example.com/oauth/callback&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;scopes&#039;&#039; || Space-separated OAuth scopes (e.g. &#039;&#039;openid profile email groups&#039;&#039;). Must include &#039;&#039;openid&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groups&#039;&#039; || Name of the token claim that carries group memberships (e.g. &#039;&#039;groups&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.group&#039;&#039; || IdP group value for this mapping. &#039;&#039;&amp;lt;idx&amp;gt;&#039;&#039; is an arbitrary stable label (numeric or descriptive).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.roleName&#039;&#039; || Obsidian role for this mapping. At least one &#039;&#039;Admin&#039;&#039; mapping is required. Valid roles: &#039;&#039;&#039;Admin&#039;&#039;&#039;, &#039;&#039;&#039;API&#039;&#039;&#039;, &#039;&#039;&#039;Write&#039;&#039;&#039;, &#039;&#039;&#039;LimitedRead&#039;&#039;&#039;, &#039;&#039;&#039;Operator&#039;&#039;&#039;, &#039;&#039;&#039;Author&#039;&#039;&#039;, and folder-based roles (&#039;&#039;&amp;lt;folder&amp;gt;-(Write|Author|Operator)&#039;&#039;).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Optional properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Default !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;displayName&#039;&#039; || (none) || Label on the SSO button: &amp;quot;Sign in with &#039;&#039;&amp;lt;displayName&amp;gt;&#039;&#039;&amp;quot;. Defaults to &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;logout.mode&#039;&#039; || &#039;&#039;local&#039;&#039; || &#039;&#039;local&#039;&#039;: invalidates Obsidian session only. &#039;&#039;rp&#039;&#039;: RP-initiated logout via IdP &#039;&#039;end_session_endpoint&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;postLogoutRedirectUri&#039;&#039; || (none) || Where to redirect after RP-initiated logout. Ignored when &#039;&#039;logout.mode=local&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.user&#039;&#039; || &#039;&#039;sub&#039;&#039; || Claim used as the internal user ID in audit logs. &#039;&#039;&#039;Strongly recommend keeping as &#039;&#039;sub&#039;&#039;&#039;&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.displayName&#039;&#039; || &#039;&#039;preferred_username&#039;&#039; || Claim shown in the Obsidian UI nav bar. Separate from &#039;&#039;claim.user&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.validateTyp&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, rejects Bearer tokens where &#039;&#039;typ&#039;&#039; is present but not &#039;&#039;at+JWT&#039;&#039;. Set &#039;&#039;false&#039;&#039; only for legacy IdPs that omit &#039;&#039;typ&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clockSkewSeconds&#039;&#039; || &#039;&#039;60&#039;&#039; || Clock skew tolerance for &#039;&#039;exp&#039;&#039;/&#039;&#039;nbf&#039;&#039; validation (seconds).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;metadata.ttlHours&#039;&#039; || &#039;&#039;24&#039;&#039; || OIDC discovery document cache TTL in hours.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;expectedAccessTokenAudience&#039;&#039; || &#039;&#039;clientId&#039;&#039; || Expected &#039;&#039;aud&#039;&#039; for Bearer access tokens. See security note below.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsSource&#039;&#039; || &#039;&#039;idtoken&#039;&#039; || Where to read groups during browser SSO: &#039;&#039;idtoken&#039;&#039;, &#039;&#039;accesstoken&#039;&#039;, or &#039;&#039;userinfo&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsBearerSource&#039;&#039; || &#039;&#039;accesstoken&#039;&#039; || Where to read groups on the Bearer path: &#039;&#039;idtoken&#039;&#039; or &#039;&#039;accesstoken&#039;&#039;. &#039;&#039;&#039;userinfo is not permitted on the Bearer path.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsValuePath&#039;&#039; || (none) || Sub-field name when groups is an array of objects (e.g. &#039;&#039;name&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsStripPrefix&#039;&#039; || (none) || Prefix to strip from each group value (e.g. &#039;&#039;/&#039;&#039; for Keycloak full-path style).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsUserinfoTimeout&#039;&#039; || &#039;&#039;5&#039;&#039; || HTTP timeout in seconds for the userinfo endpoint. Only used when &#039;&#039;claim.groupsSource=userinfo&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.prefix&#039;&#039; || (none) || Prefix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.suffix&#039;&#039;. Map is rebuilt at startup; adding a new root folder requires a restart.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.suffix&#039;&#039; || (none) || Suffix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.prefix&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;requireRole&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, browser SSO fails if IdP groups map to no Obsidian roles. Does not apply to REST Bearer tokens.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Security notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;expectedAccessTokenAudience and cross-JWT confusion&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When &#039;&#039;expectedAccessTokenAudience&#039;&#039; is not set it defaults to &#039;&#039;clientId&#039;&#039;. In many OIDC&lt;br /&gt;
configurations the id_token also carries &#039;&#039;aud=clientId&#039;&#039;, which could allow an id_token&lt;br /&gt;
to be submitted as a Bearer token. The &#039;&#039;claim.validateTyp=true&#039;&#039; default mitigates this&lt;br /&gt;
for compliant IdPs (access tokens carry &#039;&#039;typ=at+JWT&#039;&#039;; id_tokens carry &#039;&#039;typ=JWT&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
For maximum defence, set &#039;&#039;expectedAccessTokenAudience&#039;&#039; to a distinct resource-server&lt;br /&gt;
identifier if your IdP supports it.&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;claim.validateTyp=false&#039;&#039; AND &#039;&#039;expectedAccessTokenAudience&#039;&#039; is not explicitly set,&lt;br /&gt;
Obsidian logs a mandatory startup WARN advising you to set &#039;&#039;expectedAccessTokenAudience&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
===== Keycloak example =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.OAuthAuthenticator&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.issuer=http://localhost:8081/realms/obsidian&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.clientId=obsidian-client&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.clientSecret=test-client-secret-local&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.redirectUri=http://localhost:8080/oauth/callback&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.scopes=openid profile email groups&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.claim.groups=groups&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.claim.groupsSource=idtoken&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.1.group=Obsidian-Admins&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.1.roleName=Admin&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.2.group=Obsidian-Write&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.2.roleName=Write&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.3.group=Obsidian-Read&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.3.roleName=LimitedRead&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.4.group=Obsidian-API&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.4.roleName=API&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.displayName=Keycloak&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.logout.mode=local&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.requireRole=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Provider notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Microsoft Entra ID&#039;&#039;&#039;: groups claim contains GUIDs by default; configure Optional&lt;br /&gt;
Claims for display names. Accounts with 200+ groups trigger overage. Multi-tenant apps not&lt;br /&gt;
supported in v1.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auth0&#039;&#039;&#039;: opaque access tokens (Auth0 default) are not supported on the Bearer path.&lt;br /&gt;
Configure a custom API audience to get JWT access tokens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Okta&#039;&#039;&#039;: use the Custom Authorization Server URL, not the Org Authorization Server.&lt;br /&gt;
&lt;br /&gt;
===== v1 limitations =====&lt;br /&gt;
&lt;br /&gt;
* Opaque access tokens not supported on the REST/Bearer path.&lt;br /&gt;
* No &#039;&#039;jti&#039;&#039; replay protection.&lt;br /&gt;
* Microsoft Entra multi-tenant apps not supported.&lt;br /&gt;
* No post-login IdP session validity check.&lt;br /&gt;
* Dynamic role map requires restart for new root folders.&lt;br /&gt;
* Installer UI supports up to 10 static role pairs; runtime supports unlimited.&lt;br /&gt;
&lt;br /&gt;
==== SAML Authentication Properties ====&lt;br /&gt;
&lt;br /&gt;
To use SAML 2.0 SSO, set the &#039;&#039;Authenticator&#039;&#039; property to&lt;br /&gt;
&#039;&#039;com.carfey.suite.security.saml.SAMLAuthenticator&#039;&#039; and add the properties below. All&lt;br /&gt;
properties are prefixed with &#039;&#039;com.carfey.suite.security.saml.SAMLAuthenticator.&#039;&#039; (shown&lt;br /&gt;
without prefix in the table for brevity). Note the &#039;&#039;.saml.&#039;&#039; package segment in both values.&lt;br /&gt;
&lt;br /&gt;
Obsidian acts as a SAML Service Provider and supports SP-initiated browser login only. See&lt;br /&gt;
[[Authenticator#SAMLAuthenticator_(SAML_2.0_SSO)|Authenticator — SAMLAuthenticator]] for how the&lt;br /&gt;
login and logout flows behave and for the list of &#039;&#039;/saml&#039;&#039; endpoints.&lt;br /&gt;
&lt;br /&gt;
===== Required properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;entityId&#039;&#039; || SP entity ID registered at the IdP (e.g. &#039;&#039;https://app.example.com/obsidian&#039;&#039;). Obsidian also requires this value to appear in the assertion&#039;s &#039;&#039;AudienceRestriction&#039;&#039; when the IdP sends one.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;assertionConsumerServiceUrl&#039;&#039; || Exact ACS URL where the IdP posts responses. This must be Obsidian&#039;s callback endpoint, e.g. &#039;&#039;https://app.example.com/obsidian/saml/callback&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;idpMetadataUrl&#039;&#039; &#039;&#039;&#039;or&#039;&#039;&#039; &#039;&#039;idpMetadataFile&#039;&#039; || Source of the IdP federation metadata: an HTTP(S) URL, or an absolute path to a local XML file. Set exactly one of the two.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups&#039;&#039; || Name of the assertion attribute carrying group memberships (e.g. &#039;&#039;groups&#039;&#039;). Required unless &#039;&#039;dynamicRole.prefix&#039;&#039; is set instead.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.group&#039;&#039; || IdP group value for this mapping. &#039;&#039;&amp;lt;idx&amp;gt;&#039;&#039; is an arbitrary stable label (numeric or descriptive). Not needed if all roles come from &#039;&#039;dynamicRole&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.roleName&#039;&#039; || Obsidian role for this mapping. The installer requires at least one &#039;&#039;Admin&#039;&#039; mapping, and you need one in practice so that somebody can administer Obsidian. Valid roles: &#039;&#039;&#039;Admin&#039;&#039;&#039;, &#039;&#039;&#039;Write&#039;&#039;&#039;, &#039;&#039;&#039;LimitedRead&#039;&#039;&#039;, &#039;&#039;&#039;Operator&#039;&#039;&#039;, &#039;&#039;&#039;Author&#039;&#039;&#039;, and folder-based roles (&#039;&#039;&amp;lt;folder&amp;gt;-(Write|Author|Operator)&#039;&#039;). The &#039;&#039;API&#039;&#039; role has no effect on a SAML install, because the REST API is unavailable.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Because &#039;&#039;signRequests&#039;&#039; defaults to enabled, a default configuration also requires the three&lt;br /&gt;
&#039;&#039;keystore&#039;&#039; properties below. Disable &#039;&#039;signRequests&#039;&#039; only if your IdP does not require signed&lt;br /&gt;
authentication requests.&lt;br /&gt;
&lt;br /&gt;
===== Optional properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Default !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;displayName&#039;&#039; || (none) || Label on the SSO button: &amp;quot;Sign in with &#039;&#039;&amp;lt;displayName&amp;gt;&#039;&#039;&amp;quot;. Defaults to &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;signRequests&#039;&#039; || &#039;&#039;true&#039;&#039; || Signs the outbound &#039;&#039;AuthnRequest&#039;&#039; and SP &#039;&#039;LogoutRequest&#039;&#039;. Requires the three &#039;&#039;keystore&#039;&#039; properties.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.path&#039;&#039; || (none) || Path to the keystore holding the SP signing key. Required when &#039;&#039;signRequests&#039;&#039; is enabled.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.password&#039;&#039; || (none) || Keystore password. Keep out of version control.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.alias&#039;&#039; || (none) || Alias of the SP signing key within the keystore.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;wantAssertionsSigned&#039;&#039; || &#039;&#039;true&#039;&#039; || Requires the IdP to sign assertions, verified against the signing certificate in the IdP metadata.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;singleLogoutServiceUrl&#039;&#039; || (none) || SP single logout URL. When set it is published in Obsidian&#039;s SP metadata and is matched against the &#039;&#039;Destination&#039;&#039; of an incoming &#039;&#039;LogoutRequest&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;logout.mode&#039;&#039; || &#039;&#039;local&#039;&#039; || &#039;&#039;local&#039;&#039;: invalidates the Obsidian session only. &#039;&#039;slo&#039;&#039;: SP-initiated single logout via the IdP&#039;s HTTP-Redirect &#039;&#039;SingleLogoutService&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;postLogoutRedirectUri&#039;&#039; || (none) || Where to redirect after the IdP&#039;s &#039;&#039;LogoutResponse&#039;&#039;. When blank the browser returns to &#039;&#039;/&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;nameIdFormat&#039;&#039; || &#039;&#039;urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified&#039;&#039; || &#039;&#039;NameIDPolicy&#039;&#039; format requested from the IdP. A transient format logs a startup warning, because transient NameIDs cannot serve as stable user IDs.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.user&#039;&#039; || &#039;&#039;nameId&#039;&#039; || Assertion attribute used as the internal user ID in audit logs. &#039;&#039;&#039;Strongly recommend keeping as &#039;&#039;nameId&#039;&#039;&#039;&#039;&#039;; any other value logs a startup warning.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.displayName&#039;&#039; || (none) || Attribute shown in the Obsidian UI nav bar. Separate from &#039;&#039;attribute.user&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.friendlyName&#039;&#039; || (none) || Match the groups attribute on its &#039;&#039;FriendlyName&#039;&#039; instead of its &#039;&#039;Name&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.valuePath&#039;&#039; || (none) || Sub-field name when the groups attribute holds structured values.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.stripPrefix&#039;&#039; || (none) || Prefix to strip from each group value (e.g. &#039;&#039;/&#039;&#039; for Keycloak full-path style).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;metadata.ttlHours&#039;&#039; || &#039;&#039;24&#039;&#039; || IdP federation metadata cache TTL in hours. A failed refresh falls back to the cached copy with a warning.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clockSkewSeconds&#039;&#039; || &#039;&#039;60&#039;&#039; || Clock skew tolerance for &#039;&#039;NotBefore&#039;&#039; / &#039;&#039;NotOnOrAfter&#039;&#039; validation (seconds).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;forceAuthn&#039;&#039; || &#039;&#039;false&#039;&#039; || Sets &#039;&#039;ForceAuthn=&amp;quot;true&amp;quot;&#039;&#039; on the &#039;&#039;AuthnRequest&#039;&#039;, asking the IdP to reauthenticate the user.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;authnContextClassRef&#039;&#039; || (none) || Requests a specific &#039;&#039;RequestedAuthnContext&#039;&#039;. A differing context in the response is logged as a warning, not rejected.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.prefix&#039;&#039; || (none) || Prefix for auto-generated folder-role group mappings. Usable on its own. The map is built at startup, so adding a new root job folder requires a restart.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.suffix&#039;&#039; || (none) || Optional suffix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.prefix&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;requireRole&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, browser SSO fails if the user&#039;s group attributes map to no Obsidian roles.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Security notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Encrypted assertions are not supported&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Obsidian requires signed, unencrypted assertions. If your SP metadata carries no signing key&lt;br /&gt;
material, Obsidian logs a startup warning: IdPs such as AD FS may then choose to encrypt the&lt;br /&gt;
assertion, which this release cannot process. Configure the SP keystore and register the&lt;br /&gt;
generated metadata so the IdP sees a signing &#039;&#039;KeyDescriptor&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Do not disable both signature settings&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Setting &#039;&#039;signRequests&#039;&#039; and &#039;&#039;wantAssertionsSigned&#039;&#039; both to &#039;&#039;false&#039;&#039; is accepted but logs a&lt;br /&gt;
startup warning. In that state assertions are neither signed nor verified, which is not safe for&lt;br /&gt;
production.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SP-initiated flows and replay&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Obsidian issues an &#039;&#039;AuthnRequest&#039;&#039; and tracks its ID in the browser session for five minutes.&lt;br /&gt;
When the IdP&#039;s response carries &#039;&#039;InResponseTo&#039;&#039;, it must match one of those pending IDs and is&lt;br /&gt;
consumed on use, so the same response cannot be replayed. There is no persistent store of&lt;br /&gt;
processed assertion IDs, so keep assertion lifetimes short at the IdP.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Clustered deployments and IdP-initiated logout&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
An IdP-initiated &#039;&#039;LogoutRequest&#039;&#039; can only end the Obsidian session on the node that receives&lt;br /&gt;
it. Use sticky sessions if you rely on single logout across a cluster.&lt;br /&gt;
&lt;br /&gt;
===== Keycloak example =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.saml.SAMLAuthenticator&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.entityId=https://app.example.com/obsidian&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.assertionConsumerServiceUrl=https://app.example.com/obsidian/saml/callback&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.idpMetadataUrl=https://keycloak.example.com/realms/obsidian/protocol/saml/descriptor&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.path=/opt/obsidian/saml-sp.p12&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.password=changeit&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.alias=obsidian-sp&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.attribute.groups=groups&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.1.group=Obsidian-Admins&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.1.roleName=Admin&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.2.group=Obsidian-Write&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.2.roleName=Write&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.3.group=Obsidian-Read&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.3.roleName=LimitedRead&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.displayName=Keycloak&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.logout.mode=local&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.requireRole=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Provider notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Microsoft Entra ID&#039;&#039;&#039;: group claims are omitted from SAML assertions once an account belongs to&lt;br /&gt;
roughly 150 groups. Filter the groups sent to the application, or map app roles instead. Note this&lt;br /&gt;
threshold differs from the OIDC one described above.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AD FS&#039;&#039;&#039;: the federation metadata document is normally at&lt;br /&gt;
&#039;&#039;/FederationMetadata/2007-06/FederationMetadata.xml&#039;&#039;. Register signing-capable SP metadata so AD FS&lt;br /&gt;
does not encrypt the assertion, which Obsidian cannot decrypt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Okta&#039;&#039;&#039;: use the application&#039;s SAML metadata URL for &#039;&#039;idpMetadataUrl&#039;&#039;. Okta&#039;s logout endpoints&lt;br /&gt;
may be POST-only; if no HTTP-Redirect &#039;&#039;SingleLogoutService&#039;&#039; is advertised, &#039;&#039;logout.mode=slo&#039;&#039;&lt;br /&gt;
falls back to local logout.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auth0&#039;&#039;&#039;: federation metadata is served at &#039;&#039;samlp/metadata/&amp;lt;client-id&amp;gt;&#039;&#039;. Group memberships must&lt;br /&gt;
be added to the assertion with an Auth0 Action or rule.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Keycloak and other generic IdPs&#039;&#039;&#039;: Keycloak publishes a SAML descriptor at&lt;br /&gt;
&#039;&#039;/realms/&amp;lt;realm&amp;gt;/protocol/saml/descriptor&#039;&#039;. Any IdP that publishes SAML 2.0 metadata with an&lt;br /&gt;
HTTP-POST &#039;&#039;AssertionConsumerService&#039;&#039; target and a signing certificate will work.&lt;br /&gt;
&lt;br /&gt;
===== v1 limitations =====&lt;br /&gt;
&lt;br /&gt;
* The REST API is unavailable while &#039;&#039;SAMLAuthenticator&#039;&#039; is active — there is no Bearer or JWT equivalent.&lt;br /&gt;
* SP-initiated login only.&lt;br /&gt;
* Encrypted assertions are not supported.&lt;br /&gt;
* No persistent assertion replay store beyond the single-use &#039;&#039;InResponseTo&#039;&#039; match and the assertion time window.&lt;br /&gt;
* Dynamic role map (&#039;&#039;dynamicRole.prefix&#039;&#039;/&#039;&#039;suffix&#039;&#039;) requires a server restart when new root job folders are added.&lt;br /&gt;
* Installer UI supports up to 10 static role pairs; the runtime supports unlimited.&lt;br /&gt;
&lt;br /&gt;
=== SMTP Mail Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Email configuration for notifications, if desired&lt;br /&gt;
#for straight up open relay, just specify the host using&lt;br /&gt;
mail.smtp.host=smtp.myopenrelayhost.com&lt;br /&gt;
mail.smtp.port=port &lt;br /&gt;
#(standard ports are 25, 465 for SSL, 587 for TLS)&lt;br /&gt;
&lt;br /&gt;
#for using TLS and SSL, provide these as necessary&lt;br /&gt;
mail.smtp.socketFactory.port=port&lt;br /&gt;
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory&lt;br /&gt;
mail.smtp.auth=true&lt;br /&gt;
mail.smtp.user=myauthenticatedemailuser@email.com&lt;br /&gt;
mail.smtp.password=PASSWORD&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# We support JNDI lookups for email sessions. All other email properties can be excluded.&lt;br /&gt;
mail.session.jndi.path=java:comp/env/mail/session&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Database Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Database configuration&lt;br /&gt;
com.carfey.obsidian.db.url=jdbc:mysql://localhost/obsidian&lt;br /&gt;
com.carfey.obsidian.db.userId=user&lt;br /&gt;
com.carfey.obsidian.db.password=pass&lt;br /&gt;
&lt;br /&gt;
# Sample JDBC URL formats for all platforms&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:mysql://localhost:3306/obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:h2:C:/dev/workspace/obsidian;MVCC=TRUE&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:oracle:thin:@localhost:1521:obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:postgresql://localhost:5432/obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:sqlserver://localhost:1433;databaseName=obsidian&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 6.3.0, we support constructing simple JDBC urls from component properties&lt;br /&gt;
# Only supports basic URL formats noted above and does not support H2&lt;br /&gt;
#com.carfey.obsidian.db.host=localhost&lt;br /&gt;
#com.carfey.obsidian.db.port=3306&lt;br /&gt;
#com.carfey.obsidian.db.databaseName=obsidianDB&lt;br /&gt;
#com.carfey.obsidian.db.dbType=mariadb/mysql/oracle/postgresql/sqlserver&lt;br /&gt;
#com.carfey.obsidian.db.oracleSid=SIDORCL&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
# We support JNDI lookups for database connectivity.&lt;br /&gt;
com.carfey.obsidian.db.url=java:comp/env/jdbc/obsidian&lt;br /&gt;
com.carfey.obsidian.db.jndiType=mysql&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=mysql is also used for MariaDB&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=oracle&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=postgresql&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=h2&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=sqlserver&lt;br /&gt;
&lt;br /&gt;
# Table prefix may be used to add a prefix to tables and related database objects. It must be 6 or fewer characters and can contain letters or underscores.&lt;br /&gt;
com.carfey.obsidian.db.tablePrefix=OBSDN_&lt;br /&gt;
&lt;br /&gt;
# For Oracle databases, when Obsidian&#039;s tables exist in a different schema from the user specified above, or if the user does not default to the schema matching its name, specify the target schema here.&lt;br /&gt;
# For details on the required privileges to run with an alternate user, see https://wiki.obsidianscheduler.com/docs/Obsidian_Tables#Oracle_Privileges&lt;br /&gt;
# This same configuration item can be used to configure the Postgresql schema. If unspecified, the default is public.&lt;br /&gt;
# If using JNDI and Oracle/Postgresql, we recommend you set this value to ensure best performance.&lt;br /&gt;
com.carfey.obsidian.db.schema=obsidian&lt;br /&gt;
&lt;br /&gt;
#default max connections per pool if not specified is 50&lt;br /&gt;
com.carfey.obsidian.db.maxConnections=40&lt;br /&gt;
&lt;br /&gt;
#default millis timeout to retrieve available connection from pool if not specified is 2000&lt;br /&gt;
com.carfey.obsidian.db.connectionTimeout=2000&lt;br /&gt;
&lt;br /&gt;
# You can disable pooling of connections, which is suggested only if using another connection pool through JNDI. When true, maxConnections is ignored.&lt;br /&gt;
# When a JNDI data source is selected in the installer, this is set to true by default.&lt;br /&gt;
com.carfey.obsidian.db.disablePooling=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.3.0, Oracle database supports configured wait time.&lt;br /&gt;
com.carfey.obsidian.db.timedLockWaitEnabled=true (default)&lt;br /&gt;
com.carfey.obsidian.db.timedLockWaitSeconds=1 (default, prior to 4.3.0, indefinite wait)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Logging Properties ===&lt;br /&gt;
As of &#039;&#039;&#039;Obsidian 5.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Please see [https://logging.apache.org/log4j/2.x/manual/configuration.html Log4j2 configuration]&lt;br /&gt;
&lt;br /&gt;
Before &#039;&#039;&#039;Obsidian 5.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Please see [https://logging.apache.org/log4j/1.2/manual.html Log4j Configuration]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Miscellaneous Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# If you wish to change the tokens used to reference global parameters in your job configuration, use the following two values:&lt;br /&gt;
global.param.start.token={{&lt;br /&gt;
global.param.end.token=}}&lt;br /&gt;
&lt;br /&gt;
# Populated by the installer, these values can be used to initialize licence key information into the database.&lt;br /&gt;
com.carfey.obsidian.licence.key=licenceKey&lt;br /&gt;
# Name is only required for hardware-linked or site licenses. Use the name exactly as provided by Carfey Software Corporation in your license email.&lt;br /&gt;
com.carfey.obsidian.licence.name=licenceeName&lt;br /&gt;
&lt;br /&gt;
# If you have issues with classloading (e.g. in Grails or Spring), this flag enables usage of the context classloader .&lt;br /&gt;
com.carfey.jdk.useContextClassLoader=true&lt;br /&gt;
&lt;br /&gt;
# You can set the scheduler host designator name multiple times in the same JVM by using this parameter:&lt;br /&gt;
com.carfey.obsidian.schedulerDesignation=obsidian-dev&lt;br /&gt;
&lt;br /&gt;
# Job Forking can be enabled and configured per node&lt;br /&gt;
com.carfey.obsidian.jvmJobForkingEnabledOnThisNode=true&lt;br /&gt;
# This property is the location of the fork scripts. Obsidian is bundled with obsidianForkedJob.bat and obsidianForkedJob.sh. These are the expected script names.&lt;br /&gt;
com.carfey.obsidian.forkedJobScriptLocation=/Obsidian-3.0.0&lt;br /&gt;
# Use the following optional property if you need to override the default classpath that is built using the contents of the standalone directory.&lt;br /&gt;
# This allows for job forking support in embedded and even webapp deployments. Use the classpath format supported by your operating system.&lt;br /&gt;
#com.carfey.obsidian.forkedJobscriptClasspathOverride=&lt;br /&gt;
# Use the following optional property if you wish to include database configuration parameters as arguments passed to the forking scripts and the target forked Obsidian class.&lt;br /&gt;
# For example, you may need to do this if your configuration is done via System Property overrides or programmmatically in your embedded Obsidian instance.&lt;br /&gt;
#com.carfey.obsidian.jvmJobForkingIncludeDbSysParms=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.3.0, description/parameter formatting is supported. Custom formatter (com.carfey.ops.job.config.formatter.Formatter) supported via this parameter.&lt;br /&gt;
com.carfey.obsidian.formatterClass=com.carfey.ops.job.config.formatter.MarkdownFormatter (default)&lt;br /&gt;
&lt;br /&gt;
# As of 4.4.0, support for running DDL outside Obsidian and applying only the data portion of upgrades. May require incremental upgrades. Contact support for assistance. Enabled with the following:&lt;br /&gt;
com.carfey.obsidian.runner.skipDDL=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.5.0, Slack notifications can be automatically configured for FATAL, ERROR and WARN events by setting this property with a Slack incoming web hook URL. The user name property is optional and defaults to Obsidian Scheduler&lt;br /&gt;
com.carfey.obsidian.slack.webhookUrl=https://hooks.slack.com/services/XXXXXXX/YYYYYYYY/ZZZZZZZZZZ&lt;br /&gt;
com.carfey.obsidian.slack.userName=Obsidian Scheduler (prod)&lt;br /&gt;
&lt;br /&gt;
# As of 4.6.0, Obsidian supports starting a node in paused state. This can also be set as a System property.&lt;br /&gt;
com.carfey.obsidian.schedulerPausedOnStartup=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.10.0, Obsidian supports disabling script engine jobs selectively.&lt;br /&gt;
com.carfey.ops.job.script.BeanShellJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.GroovyJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.RubyJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.PythonJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.JavaScriptJob.enabled=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.0.0, Signal interrupt (supported as of 4.5.1) is disabled by default. Use configuration to enable.&lt;br /&gt;
com.carfey.obsidian.signalHandlerActiveForScheduler=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.0, you can send Obsidian events to a REST endpoint via an out-of-the-box Event Hook. The basicAuthorization property is optional, the sample showing its use for myusername:mypassword. The &amp;quot;Basic &amp;quot; prefix is added by Obsidian.&lt;br /&gt;
com.carfey.obsidian.restfulEventHook.url=https://myendpoint.com:1234/rest/obsidian_events&lt;br /&gt;
com.carfey.obsidian.restfulEventHook.basicAuthorization=bXl1c2VybmFtZTpteXBhc3N3b3Jk&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.1, maintenance jobs are automatically scheduled for new installations. Set the following property to disable this behaviour.&lt;br /&gt;
com.carfey.obsidian.skipMaintenanceJobInitialization=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.1, you can send Obsidian events to standard output and error streams using an out-of-the-box Event Hook. You can enable the default behaviour and auto-register it by adding the following configuration.&lt;br /&gt;
com.carfey.obsidian.standardOutputStreamsEventHook.enabled=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Table Prefixes ===&lt;br /&gt;
As shown in full configuration reference, a table name prefix may be specified to create Obsidian tables with names beginning with a specified string.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Table prefix may be used to add a prefix to tables and related database objects. It must be 6 or fewer characters and can contain letters or underscores.&lt;br /&gt;
com.carfey.obsidian.db.tablePrefix=OBSDN_&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Properties/Yaml File ==&lt;br /&gt;
&lt;br /&gt;
The above configuration must reside in a properties file named &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; or a yaml file named &amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; found according to the rules of [http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String) ClassLoader.getResource].  If both yaml and properties files are found, yaml is loaded first and properties values will override. Some possibilities include &amp;lt;code&amp;gt;WEB-INF/classes/com.carfey.(properties/yaml)&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;com.carfey.(properties/yaml)&amp;lt;/code&amp;gt; file in a directory which is explicitly added to the classpath, or at the root of a jar file as is done in &amp;lt;code&amp;gt;obsidian-props.jar&amp;lt;/code&amp;gt; for standalone deployments.&lt;br /&gt;
&lt;br /&gt;
Instead of using a classpath resource for properties, you can specify an external properties file using the Java system property &amp;lt;code&amp;gt;carfey.properties.file&amp;lt;/code&amp;gt;. All expected properties must be specified in either the default &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; file on the classpath or in the override file. Any properties found in both files will use the override file&#039;s values. Usage: &amp;lt;code&amp;gt;-Dcarfey.properties.file=/home/obsidian/obsidian.properties&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Instead of using a classpath resource for yaml, you can specify an external yaml file using the Java system property &amp;lt;code&amp;gt;carfey.yaml.file&amp;lt;/code&amp;gt;. All expected configuration values must be specified in either the default &amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; file on the classpath or in the override file. Any configuration values found in both files will use the override file&#039;s values. Usage: &amp;lt;code&amp;gt;-Dcarfey.yaml.file=/home/obsidian/obsidian.yaml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You may also use a programmatic properties override. Simply call &amp;lt;code&amp;gt;com.carfey.jdk.sys.Configurator.setOverride(Properties props)&amp;lt;/code&amp;gt; as early as possible in the application startup.  To quote the javadoc of this method, &#039;&#039;For use as a programmatic properties override. This must be called before any classes are accessed either through invocation or class initialization that may require access to configuration done through properties. Best if done as early as possible in code, perhaps first in an entry point class that does little else and then hands-off to existing entry point.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 4.4, you may specify configuration values as System Properties or Environment Variables. Duplicated values are resolved as follows:&lt;br /&gt;
# System Property&lt;br /&gt;
# Environment Variable - &#039;&#039;&#039;&#039;&#039;overridden by System Property&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
# Properties - &#039;&#039;&#039;&#039;&#039;overridden by Environment Variable and System Property&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Notifications Configuration ==&lt;br /&gt;
&lt;br /&gt;
Sending notifications requires SMTP configuration to be defined in the Obsidian properties file. The properties file reference at the beginning of this page includes SMTP configuration details.&lt;br /&gt;
&lt;br /&gt;
In addition to the properties file, you can configure some Notifications settings to tweak how your emails are sent. These values are configurable under the &#039;&#039;Dispatch&#039;&#039; category of the [[Admin Scheduler Settings|scheduler settings]] screen. Defaults are provided, but you can update them appropriately for your needs.  &lt;br /&gt;
&lt;br /&gt;
=== Notification sender address and DMARC ===&lt;br /&gt;
&lt;br /&gt;
The notificationFromEmail scheduler setting controls the From address used for notification emails. It is available under:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Scheduler Settings → Dispatch → notificationFromEmail&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Set this to an address belonging to your organization and authorized by the SMTP service configured for your Obsidian installation.&lt;br /&gt;
&lt;br /&gt;
Do not use ops-notification@obsidianscheduler.com when sending through your own SMTP infrastructure. Your SMTP server normally cannot authenticate mail for the obsidianscheduler.com domain. This can cause SPF, DKIM, and DMARC failures and may result in notifications being quarantined or rejected.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
scheduler-notifications@example.com&lt;br /&gt;
&lt;br /&gt;
After updating the value, trigger a test notification and confirm successful receipt.&lt;br /&gt;
&lt;br /&gt;
[[File:NotifSettings_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
== Dependent Libraries ==&lt;br /&gt;
&lt;br /&gt;
Obsidian requires a number of third-party libraries for the scheduler and the web administration application. Unless noted, libraries in a category are required for that deployment profile. The installer supports selective [[Installation_Guide#Configuring_3rd_Party_Library_Conflict_Management|conflict management]] and can omit unused optional stacks. License attribution for shipped artifacts is in the product &amp;lt;code&amp;gt;NOTICE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Overview ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Category !! Required when !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| Core || Always || Scheduler and shared runtime&lt;br /&gt;
|-&lt;br /&gt;
| Database drivers || Matching database engine || Unused drivers may be omitted at install&lt;br /&gt;
|-&lt;br /&gt;
| Mail || Email notifications enabled || Choose the javax &#039;&#039;&#039;or&#039;&#039;&#039; Jakarta stack&lt;br /&gt;
|-&lt;br /&gt;
| Scripting || Matching script job type || Groovy is modular (see Scripting table)&lt;br /&gt;
|-&lt;br /&gt;
| Web Admin || Web administration UI || Includes Markdown rendering (Flexmark)&lt;br /&gt;
|-&lt;br /&gt;
| Authentication (OAuth / OIDC) || &#039;&#039;OAuthAuthenticator&#039;&#039; active || Installer can remove when unused&lt;br /&gt;
|-&lt;br /&gt;
| Authentication (SAML 2.0) || &#039;&#039;SAMLAuthenticator&#039;&#039; active || Installer removes when SAML is not selected&lt;br /&gt;
|-&lt;br /&gt;
| Embedded Tomcat || Embedded Tomcat pack selected || Present under &amp;lt;code&amp;gt;embed-tomcat-lib/&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Core ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Component !! Artifacts !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| Obsidian || &amp;lt;code&amp;gt;obsidian.jar&amp;lt;/code&amp;gt; || Core library&lt;br /&gt;
|-&lt;br /&gt;
| Gson || &amp;lt;code&amp;gt;gson-2.10.1.jar&amp;lt;/code&amp;gt; || REST API and web administration JSON&lt;br /&gt;
|-&lt;br /&gt;
| JMustache || &amp;lt;code&amp;gt;jmustache-1.15.jar&amp;lt;/code&amp;gt; || Email templating&lt;br /&gt;
|-&lt;br /&gt;
| Log4j 2 || &amp;lt;code&amp;gt;log4j-api-2.26.0.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;log4j-core-2.26.0.jar&amp;lt;/code&amp;gt; || Logging (&amp;lt;code&amp;gt;log4j-slf4j2-impl-2.26.0.jar&amp;lt;/code&amp;gt; ships with the SAML stack)&lt;br /&gt;
|-&lt;br /&gt;
| SnakeYAML || &amp;lt;code&amp;gt;snakeyaml-2.3.jar&amp;lt;/code&amp;gt; || YAML configuration&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Database drivers ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Component !! Artifacts !! Required when&lt;br /&gt;
|-&lt;br /&gt;
| H2 || &amp;lt;code&amp;gt;h2-1.4.200.jar&amp;lt;/code&amp;gt; || Running against H2&lt;br /&gt;
|-&lt;br /&gt;
| MariaDB / MySQL || &amp;lt;code&amp;gt;mariadb-java-client-2.7.2.jar&amp;lt;/code&amp;gt; || Running against MySQL or MariaDB&lt;br /&gt;
|-&lt;br /&gt;
| SQL Server || &amp;lt;code&amp;gt;mssql-jdbc-9.2.1.jre11.jar&amp;lt;/code&amp;gt; || Running against SQL Server&lt;br /&gt;
|-&lt;br /&gt;
| Oracle || &amp;lt;code&amp;gt;ojdbc11-21.1.0.0.jar&amp;lt;/code&amp;gt; || Running against Oracle&lt;br /&gt;
|-&lt;br /&gt;
| PostgreSQL || &amp;lt;code&amp;gt;postgresql-42.2.19.jar&amp;lt;/code&amp;gt; || Running against PostgreSQL&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Mail ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Stack !! Artifacts !! Use&lt;br /&gt;
|-&lt;br /&gt;
| JavaMail (javax) || &amp;lt;code&amp;gt;activation-1.1.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;javax.mail-1.6.2.jar&amp;lt;/code&amp;gt; || Classic JavaMail notifications&lt;br /&gt;
|-&lt;br /&gt;
| Jakarta Mail || &amp;lt;code&amp;gt;jakarta.activation-api-2.1.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;jakarta.mail-api-2.1.2.jar&amp;lt;/code&amp;gt;, optional &amp;lt;code&amp;gt;angus-activation-2.0.1.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;angus-mail-2.0.2.jar&amp;lt;/code&amp;gt; || Jakarta EE email notifications&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Scripting ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Component !! Artifacts !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| BeanShell || &amp;lt;code&amp;gt;bsh-2.0b5.jar&amp;lt;/code&amp;gt; || BeanShellJob only&lt;br /&gt;
|-&lt;br /&gt;
| JRuby || &amp;lt;code&amp;gt;jruby-9.2.17.0.jar&amp;lt;/code&amp;gt; || RubyJob only&lt;br /&gt;
|-&lt;br /&gt;
| Jython || &amp;lt;code&amp;gt;jython-standalone-2.7.2.jar&amp;lt;/code&amp;gt; || PythonJob only&lt;br /&gt;
|-&lt;br /&gt;
| Groovy 4 || Modular set (17 JARs) — see list below || GroovyJob; all omitted when Groovy is disabled at install&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Groovy modular artifacts (13 runtime modules + 4 owned transitives):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;groovy-4.0.24.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;groovy-astbuilder-4.0.24.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;groovy-datetime-4.0.24.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;groovy-dateutil-4.0.24.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;groovy-json-4.0.24.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;groovy-jsr223-4.0.24.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;groovy-macro-4.0.24.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;groovy-macro-library-4.0.24.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;groovy-nio-4.0.24.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;groovy-sql-4.0.24.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;groovy-templates-4.0.24.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;groovy-typecheckers-4.0.24.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;groovy-xml-4.0.24.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;asm-analysis-9.7.1.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;asm-tree-9.7.1.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;asm-util-9.7.1.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;javaparser-core-3.26.2.jar&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Web Admin ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Component !! Artifacts !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| dom4j || &amp;lt;code&amp;gt;dom4j-2.1.3.jar&amp;lt;/code&amp;gt; || XML utilities&lt;br /&gt;
|-&lt;br /&gt;
| Flexmark || Modular set (12 JARs) — see list below || Markdown for job and parameter descriptions&lt;br /&gt;
|-&lt;br /&gt;
| JXL || &amp;lt;code&amp;gt;jxl-2.6.12.jar&amp;lt;/code&amp;gt; || Excel utilities (web administration)&lt;br /&gt;
|-&lt;br /&gt;
| OpenCSV || &amp;lt;code&amp;gt;opencsv-5.4.jar&amp;lt;/code&amp;gt; || CSV downloads in the UI&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Flexmark modular artifacts:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flexmark-0.62.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;flexmark-util-ast-0.62.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;flexmark-util-builder-0.62.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;flexmark-util-collection-0.62.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;flexmark-util-data-0.62.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;flexmark-util-dependency-0.62.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;flexmark-util-format-0.62.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;flexmark-util-html-0.62.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;flexmark-util-misc-0.62.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;flexmark-util-options-0.62.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;flexmark-util-sequence-0.62.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;flexmark-util-visitor-0.62.2.jar&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Authentication (OAuth / OIDC) ===&lt;br /&gt;
&lt;br /&gt;
Required only when &#039;&#039;OAuthAuthenticator&#039;&#039; is active.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Component !! Artifacts !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| Nimbus OAuth / OIDC || &amp;lt;code&amp;gt;oauth2-oidc-sdk-11.23.1.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nimbus-jose-jwt-10.0.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;content-type-2.3.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;lang-tag-1.7.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;json-smart-2.5.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;accessors-smart-2.5.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;jcip-annotations-1.0-1.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;asm-9.7.1.jar&amp;lt;/code&amp;gt; || OIDC SSO and JOSE+JWT (ASM is also used by Groovy when scripting is enabled)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Authentication (SAML 2.0) ===&lt;br /&gt;
&lt;br /&gt;
Required only when &#039;&#039;SAMLAuthenticator&#039;&#039; is active. The installer removes this stack when SAML is not the selected authentication type.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Component !! Artifacts !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| OpenSAML 4 || Modular set — see list below || SAML 2.0 messages, metadata, and signatures&lt;br /&gt;
|-&lt;br /&gt;
| Supporting stack || &amp;lt;code&amp;gt;java-support-8.4.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;xmlsec-2.3.4.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cryptacular-1.2.5.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;bcprov-jdk18on-1.85.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;bcpkix-jdk18on-1.85.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;bcutil-jdk18on-1.85.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;velocity-engine-core-2.3.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;commons-lang3-3.20.0.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;commons-codec-1.15.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;httpclient-4.5.14.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;httpcore-4.4.16.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;guava-32.1.3-jre.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;failureaccess-1.0.1.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;metrics-core-4.2.25.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;slf4j-api-2.0.12.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;log4j-slf4j2-impl-2.26.0.jar&amp;lt;/code&amp;gt; || Crypto, HTTP metadata fetch, Guava, metrics, and SLF4J→log4j2 bridge for OpenSAML diagnostics&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
OpenSAML modules:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;opensaml-core-4.3.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opensaml-saml-api-4.3.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opensaml-saml-impl-4.3.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opensaml-security-api-4.3.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opensaml-security-impl-4.3.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opensaml-xmlsec-api-4.3.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opensaml-xmlsec-impl-4.3.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opensaml-messaging-api-4.3.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opensaml-profile-api-4.3.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opensaml-soap-api-4.3.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opensaml-soap-impl-4.3.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opensaml-storage-api-4.3.2.jar&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded Tomcat ===&lt;br /&gt;
&lt;br /&gt;
Present only when an Embedded Tomcat pack is selected (&amp;lt;code&amp;gt;embed-tomcat-lib/&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Component !! Artifacts !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| Apache Tomcat embed || &amp;lt;code&amp;gt;tomcat-embed-core-10.1.39.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;tomcat-embed-el-10.1.39.jar&amp;lt;/code&amp;gt; || Embedded servlet runtime (Apache License 2.0)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Embedded API classpath ===&lt;br /&gt;
&lt;br /&gt;
To use the [[Embedded_API|Embedded API]], import the resources below (newer compatible versions already on your application classpath may be used). JARs are typically under the &amp;lt;code&amp;gt;standalone&amp;lt;/code&amp;gt; directory. The &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; file can be taken from &amp;lt;code&amp;gt;obsidian-properties-configuration.jar&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;obsidian-yaml-configuration.jar&amp;lt;/code&amp;gt;, or from an Obsidian WAR under &amp;lt;code&amp;gt;WEB-INF/classes&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Needed for !! Artifacts&lt;br /&gt;
|-&lt;br /&gt;
| Always || Configuration file (or configuration JAR); &amp;lt;code&amp;gt;obsidian.jar&amp;lt;/code&amp;gt;; &amp;lt;code&amp;gt;dom4j-2.1.3.jar&amp;lt;/code&amp;gt;; Flexmark modular set (see Web Admin); &amp;lt;code&amp;gt;gson-2.10.1.jar&amp;lt;/code&amp;gt;; &amp;lt;code&amp;gt;log4j-api-2.26.0.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;log4j-core-2.26.0.jar&amp;lt;/code&amp;gt;; appropriate JDBC driver&lt;br /&gt;
|-&lt;br /&gt;
| Notifications || &amp;lt;code&amp;gt;jmustache-1.15.jar&amp;lt;/code&amp;gt; plus the javax &#039;&#039;&#039;or&#039;&#039;&#039; Jakarta mail stack from the Mail table&lt;br /&gt;
|-&lt;br /&gt;
| Scripting jobs || BeanShell, JRuby, Jython, and/or Groovy modular set as used by your jobs&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Library coordinates in Obsidian 5.x and 6.x ===&lt;br /&gt;
&lt;br /&gt;
Current coordinates are listed above. The table below records notable library coordinates used in Obsidian 5.x and 6.x before they were replaced. Pre-5.0.0 history is not kept on this page (see page history if needed).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Replaced in !! Component !! Artifacts then in use&lt;br /&gt;
|-&lt;br /&gt;
| 7.0.0 || Log4j || &amp;lt;code&amp;gt;log4j-api-2.17.1.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;log4j-core-2.17.1.jar&amp;lt;/code&amp;gt;. Within 5.0.x patch releases before 5.0.3: 2.14.1 (5.0.0), 2.15.0 (5.0.1), 2.16.0 (5.0.2)&lt;br /&gt;
|-&lt;br /&gt;
| 7.0.0 || Groovy || &amp;lt;code&amp;gt;apache-groovy-all-4.0.24.jar&amp;lt;/code&amp;gt; (6.x). Before 6.0.0: &amp;lt;code&amp;gt;groovy-all-3.0.7.jar&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 7.0.0 || Flexmark || &amp;lt;code&amp;gt;flexmark-0.62.2.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;flexmark-util-0.62.2.jar&amp;lt;/code&amp;gt; (uber util JAR)&lt;br /&gt;
|-&lt;br /&gt;
| 7.0.0 || OAuth / OIDC, SAML 2.0, Embedded Tomcat || Not shipped before 7.0.0&lt;br /&gt;
|-&lt;br /&gt;
| 5.4.0 || Gson || &amp;lt;code&amp;gt;gson-2.8.6.jar&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 5.2.0 || Jakarta Activation || &amp;lt;code&amp;gt;jakarta.activation-1.2.1.jar&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 5.0.0 || Mail, scripting, JDBC, and related || Baseline 5.0.0 set included &amp;lt;code&amp;gt;javax.mail-1.5.5.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;dom4j-1.6.1.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;gson-2.7.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;bsh-2.0b4.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;groovy-all-2.4.14.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;jython-standalone-2.7.0.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;jruby-complete-9.2.7.0.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;jmustache-1.12.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;h2-1.4.192.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;log4j-1.2.9.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mariadb-java-client-1.4.5.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;jtds-1.3.1.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ojdbc7-12.1.0.1.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;postgresql-9.4.1208.jre7.jar&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;opencsv-3.8.jar&amp;lt;/code&amp;gt;, and (from 4.3.0) Flexmark 0.19.6&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Disabling Job Execution &amp;amp; Scheduling in the Web Application ==&lt;br /&gt;
See the instructions within the [[Getting_Started#Disabling_Job_Scheduling_in_the_Web_Application|Getting Started Guide]].&lt;br /&gt;
&lt;br /&gt;
== Disabling Automatic Database Updates ==&lt;br /&gt;
See the instructions within the [[Getting_Started#Disabling_Automatic_Database_Updates|Getting Started Guide]].&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Installation_Guide&amp;diff=4167</id>
		<title>Installation Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Installation_Guide&amp;diff=4167"/>
		<updated>2026-07-31T15:37:28Z</updated>

		<summary type="html">&lt;p&gt;Craig: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;This installation guide generally applies to Obsidian versions 4.0 and newer but is specific to 7.0. You may see a few minor differences in 4.x, 5.x and 6.x releases.&lt;br /&gt;
&lt;br /&gt;
This installation guide is a companion to the Obsidian Installer UI. Its purpose is to provide additional detail as to the meaning of various inputs and to provide guidance on advanced usage of the installer.&lt;br /&gt;
&lt;br /&gt;
The installer itself installs and configures the artifacts. The configured artifacts will have their [[Advanced_Configuration#Properties_File|Properties]] file configured according to the choices made during the installation process. You may always change these later or use one of the [[Advanced_Configuration#Properties_File|override]] mechanisms.&lt;br /&gt;
== Running the Obsidian Installer ==&lt;br /&gt;
The installer is an executable JAR file in the Obsidian download zip package available on our [https://web.obsidianscheduler.com/download/ download page].&lt;br /&gt;
&lt;br /&gt;
The installer can be run from the command line as a graphical user interface using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar&amp;lt;/code&amp;gt; or in interactive console mode using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar -console&amp;lt;/code&amp;gt;. Note that you will have to replace the JAR file name with the actual versioned name in your installation.&lt;br /&gt;
&lt;br /&gt;
On some platforms, simple double-clicking the JAR file will start it in graphical interface mode.&lt;br /&gt;
&lt;br /&gt;
If you are doing a version upgrade of Obsidian or are otherwise uninterested in actually configuring the artifacts, you can run the quick start mode to get the default configured artifacts using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar h2-tomcat-quick-start.xml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Obsidian Installer Artifacts ==&lt;br /&gt;
&lt;br /&gt;
The Obsidian Installer installs and configures a number of artifacts. You can choose which artifacts to create, but most users can leave the default options selected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Installation-Packages.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Obsidian WAR&#039;&#039;&#039; - This is the Obsidian Web Admin UI with scheduler component enabled. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/obsidian.war!/WEB-INF/classes/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Standalone Admin WAR&#039;&#039;&#039; - This is the Obsidian Web Admin UI with scheduler component disabled. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/standaloneObsidianAdmin.war!/WEB-INF/classes/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Standalone Obsidian Runtime&#039;&#039;&#039; - This is a runtime folder containing the libraries and configuration necessary for running the Obsidian Scheduler component either as a standalone module or for use as an Embedded Scheduler in your application. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/standalone/obsidian-props.jar!/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian License Proxy&#039;&#039;&#039; - Allows for local license leasing from a [[Key_Server_Proxy|Key Server Proxy]] that ultimately leases licenses from the Obsidian License server.&lt;br /&gt;
# &#039;&#039;&#039;Documentation&#039;&#039;&#039; - README and Embedded API Javadoc.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; — An embedded Apache Tomcat runtime for running Obsidian locally without an external servlet container. Requires the Obsidian WAR pack.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; — An embedded Apache Tomcat runtime for running the standalone admin WAR locally. Requires the Obsidian Standalone Admin WAR pack.&lt;br /&gt;
# &#039;&#039;&#039;License&#039;&#039;&#039; - Obsidian and 3rd party license information.&lt;br /&gt;
&lt;br /&gt;
==== Deployment options ====&lt;br /&gt;
&lt;br /&gt;
Obsidian 7.0.0 offers four deployment profiles in the installer:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Obsidian WAR&#039;&#039;&#039; — deploy &#039;&#039;&#039;obsidian.war&#039;&#039;&#039; to your own servlet container (Tomcat, WebLogic, etc.).&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; — adds an embedded Tomcat runtime and &#039;&#039;&#039;webObsidian&#039;&#039;&#039; scripts to run &#039;&#039;&#039;obsidian.war&#039;&#039;&#039; locally (&#039;&#039;&#039;start scheduler&#039;&#039;&#039;). &#039;&#039;&#039;Requires&#039;&#039;&#039; the Obsidian WAR pack.&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Standalone Admin WAR&#039;&#039;&#039; — admin console WAR for an external servlet container.&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; — embedded runtime for the admin WAR (&#039;&#039;&#039;start adminOnly&#039;&#039;&#039;). &#039;&#039;&#039;Requires&#039;&#039;&#039; the Standalone Admin WAR pack.&lt;br /&gt;
&lt;br /&gt;
Embedded Tomcat does &#039;&#039;&#039;not&#039;&#039;&#039; replace the WAR on disk — it runs the installed WAR via the helper. Do not select an embed pack without its matching WAR pack. For how to start, stop and change the ports of the embedded runtime, see [[Getting_Started#Starting_Obsidian_with_embedded_Tomcat|Starting Obsidian with embedded Tomcat]].&lt;br /&gt;
&lt;br /&gt;
==== Linux desktop and systemd integration ====&lt;br /&gt;
&lt;br /&gt;
On a Unix host, an embedded Tomcat pack also copies a &#039;&#039;&#039;linux/&#039;&#039;&#039; directory under your install path holding user-level systemd and desktop-entry templates, and the installer offers to activate them. Nothing is installed system-wide and no root privileges are required.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Embedded Tomcat - Linux integration (optional)&#039;&#039;&#039; panel carries the checkbox &#039;&#039;&#039;Install Linux desktop and user systemd integration&#039;&#039;&#039; (unchecked by default). Tick it to have the installer run the integration script for you; the option only takes effect if you also selected an embedded Tomcat pack. Leaving it unchecked still copies the templates, so you can opt in later by running this from the install directory:&lt;br /&gt;
&lt;br /&gt;
 ./linux/install-embedded-linux-integration.sh &amp;quot;$PWD&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Either route writes two files, for the installing user only:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/.config/systemd/user/obsidian-embedded@scheduler.service&#039;&#039;&lt;br /&gt;
* &#039;&#039;~/.local/share/applications/obsidian-embedded.desktop&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;XDG_CONFIG_HOME&#039;&#039; and &#039;&#039;XDG_DATA_HOME&#039;&#039; are honoured when set.)&lt;br /&gt;
&lt;br /&gt;
For an automated (headless) install, request the integration with:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;entry key=&amp;quot;embed.linux.integration&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This panel is &#039;&#039;&#039;not shown on Windows installs&#039;&#039;&#039; — the systemd and desktop templates are Linux-only. On Windows, run &#039;&#039;&#039;webObsidian.bat&#039;&#039;&#039; manually or wrap it with your own service tooling.&lt;br /&gt;
&lt;br /&gt;
Installing these files does not enable or start anything. To enable, stop, reconfigure or remove the service, see [[Getting_Started#Running_Obsidian_as_a_Linux_user_service|Running Obsidian as a Linux user service]].&lt;br /&gt;
&lt;br /&gt;
== Obsidian Configuration ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Obsidian.png]]&lt;br /&gt;
&lt;br /&gt;
First, you must choose what [[Authenticator]] mechanism will be used within Obsidian. Obsidian supports five authentication methods: native (database), LDAP, OAuth (OIDC), SAML 2.0, and custom. Select &#039;&#039;Native (Database)&#039;&#039;, &#039;&#039;LDAP&#039;&#039;, &#039;&#039;OAuth (OIDC)&#039;&#039;, &#039;&#039;SAML 2.0&#039;&#039;, or &#039;&#039;Other&#039;&#039; (custom). &#039;&#039;Native (Database)&#039;&#039; requires no additional configuration and is what most users will select at least to start.&lt;br /&gt;
&lt;br /&gt;
Next, you&#039;ll want to select the email usage type. We highly recommend you configure Obsidian for email use as it will allow you to benefit from the event [[Event_Notifications|notification]] and [[Admin_Notifications|subscription]] support in Obsidian.&lt;br /&gt;
&lt;br /&gt;
Then you&#039;ll configure the log file location, license key (optional) and registered company name (optional). Registered company name is only required for our Site License users.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== OAuth / OIDC Configuration ====&lt;br /&gt;
&lt;br /&gt;
If you selected &#039;&#039;&#039;OAuth (OIDC)&#039;&#039;&#039; as the authentication type, the installer displays additional panels for OAuth configuration.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Provider and Common Settings =====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-OAuth_OIDC.png]]&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Identity Provider&#039;&#039;&#039; — Select your provider: Keycloak, Microsoft Entra ID, Okta, Auth0, or Generic OIDC.&lt;br /&gt;
* &#039;&#039;&#039;SSO Button label&#039;&#039;&#039; — Label shown on the &amp;quot;Sign in with…&amp;quot; button (e.g. &#039;&#039;Keycloak&#039;&#039;, &#039;&#039;Acme Corp SSO&#039;&#039;). If left blank the button reads &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
* &#039;&#039;&#039;Redirect URI&#039;&#039;&#039; — The callback URL Obsidian receives after IdP login. Must be registered at the IdP (e.g. &#039;&#039;https://app.example.com/oauth/callback&#039;&#039;).&lt;br /&gt;
* &#039;&#039;&#039;Logout mode&#039;&#039;&#039; — &#039;&#039;local&#039;&#039; (default) or &#039;&#039;rp&#039;&#039; (RP-initiated logout via the IdP&#039;s end_session_endpoint).&lt;br /&gt;
&lt;br /&gt;
===== Provider-Specific Settings =====&lt;br /&gt;
&lt;br /&gt;
Fill in the connection details for your chosen provider.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Keycloak.png]]&lt;br /&gt;
&lt;br /&gt;
;Keycloak&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — e.g. &#039;&#039;https://keycloak.example.com/realms/myrealm&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from the Keycloak client settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — recommended: &#039;&#039;openid profile email groups&#039;&#039;. &lt;br /&gt;
Note: Keycloak does not include a &#039;&#039;groups&#039;&#039; claim by default — a Group Membership mapper must be configured in the client&#039;s Mappers tab.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Microsoft-Entra-ID.png]]&lt;br /&gt;
&lt;br /&gt;
;Microsoft Entra ID&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — &#039;&#039;https://login.microsoftonline.com/&amp;lt;tenant-id&amp;gt;/v2.0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; — the Application (client) ID from the Azure portal.&lt;br /&gt;
* &#039;&#039;&#039;Client Secret&#039;&#039;&#039; — a client secret value from the Azure portal.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email&#039;&#039;. &lt;br /&gt;
Note: the &#039;&#039;groups&#039;&#039; claim contains GUIDs by default; configure Optional Claims in the Azure portal to receive group display names. &lt;br /&gt;
&#039;&#039;&#039;Multi-tenant applications are not supported in Obsidian 7.0.0.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Okta.png]]&lt;br /&gt;
&lt;br /&gt;
;Okta&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — use the &#039;&#039;&#039;Custom Authorization Server&#039;&#039;&#039; URL, e.g. &#039;&#039;https://&amp;lt;domain&amp;gt;.okta.com/oauth2/default&#039;&#039;. Do &#039;&#039;&#039;not&#039;&#039;&#039; use the Org Authorization Server URL.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from Okta application settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email groups&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Auth0.png]]&lt;br /&gt;
&lt;br /&gt;
;Auth0&lt;br /&gt;
* &#039;&#039;&#039;Domain / Issuer URL&#039;&#039;&#039; — e.g. &#039;&#039;https://&amp;lt;domain&amp;gt;.auth0.com/&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from Auth0 application settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email&#039;&#039;.&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; Auth0 issues opaque access tokens by default which are &#039;&#039;&#039;not supported&#039;&#039;&#039; on Obsidian&#039;s REST/Bearer path — configure a custom API in Auth0 to get JWT access tokens.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Generic_OIDC.png]]&lt;br /&gt;
&lt;br /&gt;
;Generic OIDC&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — the base URL; Obsidian appends &#039;&#039;/.well-known/openid-configuration&#039;&#039; to discover endpoints.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from the provider.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — at minimum &#039;&#039;openid&#039;&#039;; add &#039;&#039;profile&#039;&#039; and your groups scope as needed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Group → Role Mapping =====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-OAuth-Group-Role-Mapping.png]]&lt;br /&gt;
&lt;br /&gt;
The final OAuth panel maps IdP group values to Obsidian roles. &#039;&#039;&#039;Require role assignment&#039;&#039;&#039; (default yes) — when enabled, users with no matching roles cannot complete browser login. At least one mapping to the &#039;&#039;&#039;Admin&#039;&#039;&#039; role is required. The installer supports up to 10 group→role pairs; additional pairs can be added directly to the configuration file after install. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties]] for the full property reference.&lt;br /&gt;
&lt;br /&gt;
===== Registering the redirect URI at the IdP =====&lt;br /&gt;
&lt;br /&gt;
Before starting Obsidian, register the redirect URI shown in the installer at your IdP: Keycloak — Client → Settings → Valid Redirect URIs; Entra ID — App Registration → Authentication → Redirect URIs; Okta — Application → General Settings → Sign-in Redirect URIs; Auth0 — Application → Settings → Allowed Callback URLs. The URI must match &#039;&#039;com.carfey.suite.security.OAuthAuthenticator.redirectUri&#039;&#039; exactly, including scheme and port.&lt;br /&gt;
&lt;br /&gt;
==== SAML 2.0 Configuration ====&lt;br /&gt;
&lt;br /&gt;
If you selected &#039;&#039;&#039;SAML 2.0&#039;&#039;&#039; as the authentication type, the installer displays additional panels that configure the Service Provider identity, SAML signing credentials, federation metadata, attribute options, and group→role mapping.&lt;br /&gt;
&lt;br /&gt;
===== Service Provider =====&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;SP Entity ID&#039;&#039;&#039; — HTTPS URI registered as the SAML SP entity (must match IdP registrations).&lt;br /&gt;
* &#039;&#039;&#039;Assertion Consumer Service URL&#039;&#039;&#039; — HTTPS ACS endpoint where SAML responses arrive (HTTP-POST); aligns with the ACS in &#039;&#039;/saml/metadata&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;SP Single Logout Service URL&#039;&#039;&#039; — Optional HTTPS URL for IdP-initiated SLO when logout mode targets IdP-mediated logout.&lt;br /&gt;
* &#039;&#039;&#039;SSO button label&#039;&#039;&#039; — Required label shown on &#039;&#039;&#039;Sign in with …&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Logout mode&#039;&#039;&#039; — &#039;&#039;&#039;Local&#039;&#039;&#039; (Obsidian-only) or &#039;&#039;&#039;IdP / SLO&#039;&#039;&#039; (participates in SAML single logout when advertised).&lt;br /&gt;
* &#039;&#039;&#039;Post-logout redirect URI&#039;&#039;&#039; — Optional HTTPS landing URI after &#039;&#039;LogoutResponse&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Keystore path / alias / passwords&#039;&#039;&#039; — Required when signing SAML requests (password field is dual-entry validated).&lt;br /&gt;
* &#039;&#039;&#039;Sign SAML authentication requests&#039;&#039;&#039; — Toggle (installer default &#039;&#039;&#039;true&#039;&#039;&#039;).&lt;br /&gt;
* &#039;&#039;&#039;Require signed SAML assertions&#039;&#039;&#039; — Toggle (recommended &#039;&#039;&#039;true&#039;&#039;&#039; for production).&lt;br /&gt;
&lt;br /&gt;
===== Identity Provider =====&lt;br /&gt;
&lt;br /&gt;
Choose the IdP type: Microsoft Entra ID (Azure AD), AD FS (on-premises), Okta, Auth0, or Generic SAML 2.0 (covers Keycloak &#039;&#039;/protocol/saml/descriptor&#039;&#039;, Ping, Google Workspace, and similar). Follow-on screens seed example metadata URLs and default attribute names.&lt;br /&gt;
&lt;br /&gt;
Provide &#039;&#039;&#039;either&#039;&#039;&#039; an HTTPS federation metadata URL &#039;&#039;&#039;or&#039;&#039;&#039; a local federation XML absolute path — choose exactly one. Later panels cover attribute options (&#039;&#039;friendlyName&#039;&#039;, &#039;&#039;valuePath&#039;&#039;, &#039;&#039;stripPrefix&#039;&#039;, &#039;&#039;attribute.user&#039;&#039;, &#039;&#039;attribute.displayName&#039;&#039;, &#039;&#039;ForceAuthn&#039;&#039;, &#039;&#039;RequestedAuthnContext&#039;&#039;, NameID format).&lt;br /&gt;
&lt;br /&gt;
===== Group → Role Mapping =====&lt;br /&gt;
&lt;br /&gt;
The closing panels map IdP group attribute values to Obsidian roles, including &#039;&#039;&#039;Require role assignment&#039;&#039;&#039; (default yes) and optional &#039;&#039;dynamicRole.prefix&#039;&#039; / &#039;&#039;suffix&#039;&#039;. At least one mapping to the &#039;&#039;&#039;Admin&#039;&#039;&#039; role is required. See [[Advanced_Configuration#SAML_Authentication_Properties|SAML Authentication Properties]] for the full property reference.&lt;br /&gt;
&lt;br /&gt;
===== SP metadata artefacts =====&lt;br /&gt;
&lt;br /&gt;
During install the tooling writes &#039;&#039;&#039;sp-metadata.xml&#039;&#039;&#039; under the Obsidian deployment directory; the same metadata is available live at &#039;&#039;&#039;/saml/metadata&#039;&#039;&#039; after startup. Register whichever artefact your IdP admin prefers.&lt;br /&gt;
&lt;br /&gt;
===== Before you complete install =====&lt;br /&gt;
&lt;br /&gt;
* Ensure ACS and Entity ID &#039;&#039;&#039;https&#039;&#039;&#039; URLs are reachable from IdPs (align with reverse proxies).&lt;br /&gt;
* For Entra &#039;&#039;&#039;150+&#039;&#039;&#039; group memberships in SAML assertions, expect attribute suppression — filter groups or map assigned app roles instead. (OIDC uses a &#039;&#039;&#039;200&#039;&#039;&#039; group threshold; the numbers differ by protocol.)&lt;br /&gt;
&lt;br /&gt;
==== LDAP Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.LDAP.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve selected LDAP Authentication, this screen takes you through configuring the server address and the various elements used to grant access. You should familiarize yourself with Obsidian [[Authenticator#Roles|Roles]]. The &#039;&#039;Access DN&#039;&#039; configuration element grants Read access to Obsidian application. You may use the same DN for more than one Role should you so wish.&lt;br /&gt;
&lt;br /&gt;
==== Custom Authenticator Configuration====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.CustomAuthenticator.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have chosen to use your own authentication mechanism, you must enter the fully qualified classname here.&lt;br /&gt;
&lt;br /&gt;
==== Database Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Database.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Next comes database configuration. If you are using JNDI, leave the username and password fields blank. If using JDBC URL, username and password fields are required.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;Database connections per instance&#039;&#039; and &#039;&#039;Database connection timeout(millis)&#039;&#039; fields are required and provide directive to the connection pool.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Database table name prefix&#039;&#039; is optional and is typically used when Obsidian will be colocated in an existing database/schema.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Database schema (Oracle/PostgreSQL)&#039;&#039; - This is used to allow for an alternate schema other than the default user&#039;s schema. We also recommend setting this value with Oracle/PostgreSQL when using JNDI as it allows more efficient database metadata loading.&lt;br /&gt;
&lt;br /&gt;
By default, the Obsidian installation will only include the JDBC libraries necessary for your particular database. At times you may wish to include the others to be able to change between databases. In those cases, check &#039;&#039;Include all supported JDBC libs&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Email Configuration ====&lt;br /&gt;
&lt;br /&gt;
If not using JNDI for mail sessions, provide the server and authentication details as required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.SSL.Email.png]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.TLS.Email.png]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.Open.Email.png]]&lt;br /&gt;
&lt;br /&gt;
==== JNDI Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.JNDI.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have selected JNDI for mail sessions, provide the JNDI path here. If you are using JNDI for Database connections, specify the database type.&lt;br /&gt;
&lt;br /&gt;
==== Configuring 3rd Party Library Conflict Management ====&lt;br /&gt;
[[Image:JarJar-Obsidian-6.0.0.png]]&lt;br /&gt;
&lt;br /&gt;
The Obsidian installer allows you to use [https://code.google.com/archive/p/jarjar JarJar] to handle potential conflicts between Obsidian&#039;s use of 3rd party libraries and versions used within your application. On this screen, simply select which libraries to which you wish to apply the JarJar bytecode modification process. This is optional and is skipped if no libraries are selected. Any jars not listed for which you are using more recent versions can be used and are therefore not included as options in the JarJar processing.&lt;br /&gt;
&lt;br /&gt;
==== Selecting Script Libraries ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-6.0.0-Choose-Scripting-Libraries.png]]&lt;br /&gt;
&lt;br /&gt;
The Obsidian installer allows you to select which script libraries you wish to include. Jython and JRuby are incompatible with each other, so the installer will only allow one of the two selected. If all script libraries are deselected, the only script jobs that can be run in Obsidian will be Javascript jobs as that engine is built into Java.&lt;br /&gt;
&lt;br /&gt;
==== Choosing Email Support ====&lt;br /&gt;
As of Obsidian 5.2.0, you can choose between JavaMail (javax) and Jakarta mail (Jakarta EE) implementations. When choosing Jakarta, you can also choose to bundle the Angus Jakarta compatible implementation. &lt;br /&gt;
&lt;br /&gt;
If you are using JNDI, this will only include the relevant support in the Obsidian WAR artifacts and the actual libraries in use must be provided by you and bundled with your container. If the standalone scheduler is selected along with JNDI, JNDI is assumed only relevant for WARs. As such, default libraries for either JavaMail or Jakarta will be included in the standalone installation.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.EmailImplementation.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
==== Additional configuration items ====&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 5.5.0, if you require any additional configuration items such as additional appenders/loggers or event hook configurations, you can add them here.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.5.0.Extra.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
==== Offline or restricted-network install ====&lt;br /&gt;
&lt;br /&gt;
Obsidian 7.0.0 resolves third-party libraries when you run the installer. For environments without direct access to Maven Central:&lt;br /&gt;
&lt;br /&gt;
# Pre-stage a local dependency cache containing every coordinate listed in the Obsidian dependency inventory for your release (contact Carfey support or your account team for the checklist file matching &#039;&#039;&#039;Obsidian-Install-7.0.0.jar&#039;&#039;&#039;).&lt;br /&gt;
# Copy the cache to the install host. Two layouts are supported:&lt;br /&gt;
#* &#039;&#039;&#039;Flat:&#039;&#039;&#039; one file per installed JAR name directly under the cache root.&lt;br /&gt;
#* &#039;&#039;&#039;File Maven repo:&#039;&#039;&#039; &#039;&#039;&#039;&amp;lt;group&amp;gt;/&amp;lt;artifact&amp;gt;/&amp;lt;version&amp;gt;/&amp;lt;installed-filename&amp;gt;.jar&#039;&#039;&#039;&lt;br /&gt;
# Run the installer with offline mode and the cache directory:&lt;br /&gt;
&lt;br /&gt;
 java -Dobsidian.install.deps.offline=true -Dobsidian.install.deps.cache.dir=/path/to/cache -jar Obsidian-Install-7.0.0.jar&lt;br /&gt;
&lt;br /&gt;
If a required library is missing from the cache, the install stops with an error naming the coordinate — add that artifact to the cache and re-run.&lt;br /&gt;
&lt;br /&gt;
===== Corporate Maven mirror (online) =====&lt;br /&gt;
&lt;br /&gt;
When your site mirrors Maven Central, point the installer at your repository base URL:&lt;br /&gt;
&lt;br /&gt;
 java -Dobsidian.install.deps.repo.url=https://nexus.example.com/repository/maven-public/ -jar Obsidian-Install-7.0.0.jar&lt;br /&gt;
&lt;br /&gt;
The installer reuses resolved artifacts for the duration of one install session.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Completing the Installation ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Finished.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Once you&#039;ve completed the Installation and Configuration screens, fully configured Obsidian Scheduler artifacts are now ready for you to use in the installation path you selected.&lt;br /&gt;
&lt;br /&gt;
Should you wish to automate future installations with the same configuration, click &#039;&#039;Generate an automatic installation script&#039;&#039;. This will prompt you to save an XML file that can be used for future installations using the automated install procedure &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar my-obsidian-configuration.xml&amp;lt;/code&amp;gt;. Every effort is made to ensure compatibility of these automated install files between versions. Any incompatibility will be noted in the [[Release_Notes|Release Notes]]. These automated installer files can also be used as templates for other environments, modifying them as necessary.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Installation_Guide&amp;diff=4166</id>
		<title>Installation Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Installation_Guide&amp;diff=4166"/>
		<updated>2026-07-31T15:34:09Z</updated>

		<summary type="html">&lt;p&gt;Craig: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;This installation guide generally applies to Obsidian versions 4.0 and newer but is specific to 7.0. You may see a few minor differences in 4.x, 5.x and 6.x releases.&lt;br /&gt;
&lt;br /&gt;
This installation guide is a companion to the Obsidian Installer UI. Its purpose is to provide additional detail as to the meaning of various inputs and to provide guidance on advanced usage of the installer.&lt;br /&gt;
&lt;br /&gt;
The installer itself installs and configures the artifacts. The configured artifacts will have their [[Advanced_Configuration#Properties_File|Properties]] file configured according to the choices made during the installation process. You may always change these later or use one of the [[Advanced_Configuration#Properties_File|override]] mechanisms.&lt;br /&gt;
== Running the Obsidian Installer ==&lt;br /&gt;
The installer is an executable JAR file in the Obsidian download zip package available on our [https://web.obsidianscheduler.com/download/ download page].&lt;br /&gt;
&lt;br /&gt;
The installer can be run from the command line as a graphical user interface using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar&amp;lt;/code&amp;gt; or in interactive console mode using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar -console&amp;lt;/code&amp;gt;. Note that you will have to replace the JAR file name with the actual versioned name in your installation.&lt;br /&gt;
&lt;br /&gt;
On some platforms, simple double-clicking the JAR file will start it in graphical interface mode.&lt;br /&gt;
&lt;br /&gt;
If you are doing a version upgrade of Obsidian or are otherwise uninterested in actually configuring the artifacts, you can run the quick start mode to get the default configured artifacts using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar h2-tomcat-quick-start.xml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Obsidian Installer Artifacts ==&lt;br /&gt;
&lt;br /&gt;
The Obsidian Installer installs and configures a number of artifacts. You can choose which artifacts to create, but most users can leave the default options selected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Installation-Packages.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Obsidian WAR&#039;&#039;&#039; - This is the Obsidian Web Admin UI with scheduler component enabled. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/obsidian.war!/WEB-INF/classes/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Standalone Admin WAR&#039;&#039;&#039; - This is the Obsidian Web Admin UI with scheduler component disabled. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/standaloneObsidianAdmin.war!/WEB-INF/classes/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Standalone Obsidian Runtime&#039;&#039;&#039; - This is a runtime folder containing the libraries and configuration necessary for running the Obsidian Scheduler component either as a standalone module or for use as an Embedded Scheduler in your application. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/standalone/obsidian-props.jar!/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian License Proxy&#039;&#039;&#039; - Allows for local license leasing from a [[Key_Server_Proxy|Key Server Proxy]] that ultimately leases licenses from the Obsidian License server.&lt;br /&gt;
# &#039;&#039;&#039;Documentation&#039;&#039;&#039; - README and Embedded API Javadoc.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; — An embedded Apache Tomcat runtime for running Obsidian locally without an external servlet container. Requires the Obsidian WAR pack.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; — An embedded Apache Tomcat runtime for running the standalone admin WAR locally. Requires the Obsidian Standalone Admin WAR pack.&lt;br /&gt;
# &#039;&#039;&#039;License&#039;&#039;&#039; - Obsidian and 3rd party license information.&lt;br /&gt;
&lt;br /&gt;
==== Deployment options ====&lt;br /&gt;
&lt;br /&gt;
Obsidian 7.0.0 offers four deployment profiles in the installer:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Obsidian WAR&#039;&#039;&#039; — deploy &#039;&#039;&#039;obsidian.war&#039;&#039;&#039; to your own servlet container (Tomcat, WebLogic, etc.).&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; — adds an embedded Tomcat runtime and &#039;&#039;&#039;webObsidian&#039;&#039;&#039; scripts to run &#039;&#039;&#039;obsidian.war&#039;&#039;&#039; locally (&#039;&#039;&#039;start scheduler&#039;&#039;&#039;). &#039;&#039;&#039;Requires&#039;&#039;&#039; the Obsidian WAR pack.&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Standalone Admin WAR&#039;&#039;&#039; — admin console WAR for an external servlet container.&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; — embedded runtime for the admin WAR (&#039;&#039;&#039;start adminOnly&#039;&#039;&#039;). &#039;&#039;&#039;Requires&#039;&#039;&#039; the Standalone Admin WAR pack.&lt;br /&gt;
&lt;br /&gt;
Embedded Tomcat does &#039;&#039;&#039;not&#039;&#039;&#039; replace the WAR on disk — it runs the installed WAR via the helper. Do not select an embed pack without its matching WAR pack. For how to start, stop and change the ports of the embedded runtime, see [[Getting_Started#Starting_Obsidian_with_embedded_Tomcat|Starting Obsidian with embedded Tomcat]].&lt;br /&gt;
&lt;br /&gt;
==== Linux desktop and systemd integration ====&lt;br /&gt;
&lt;br /&gt;
On a Unix host, an embedded Tomcat pack also copies a &#039;&#039;&#039;linux/&#039;&#039;&#039; directory under your install path holding user-level systemd and desktop-entry templates, and the installer offers to activate them. Nothing is installed system-wide and no root privileges are required.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Embedded Tomcat - Linux integration (optional)&#039;&#039;&#039; panel carries the checkbox &#039;&#039;&#039;Install Linux desktop and user systemd integration&#039;&#039;&#039; (unchecked by default). Tick it to have the installer run the integration script for you; the option only takes effect if you also selected an embedded Tomcat pack. Leaving it unchecked still copies the templates, so you can opt in later by running this from the install directory:&lt;br /&gt;
&lt;br /&gt;
 ./linux/install-embedded-linux-integration.sh &amp;quot;$PWD&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Either route writes two files, for the installing user only:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/.config/systemd/user/obsidian-embedded@scheduler.service&#039;&#039;&lt;br /&gt;
* &#039;&#039;~/.local/share/applications/obsidian-embedded.desktop&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;XDG_CONFIG_HOME&#039;&#039; and &#039;&#039;XDG_DATA_HOME&#039;&#039; are honoured when set.)&lt;br /&gt;
&lt;br /&gt;
For an automated (headless) install, request the integration with:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;entry key=&amp;quot;embed.linux.integration&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This panel is &#039;&#039;&#039;not shown on Windows installs&#039;&#039;&#039; — the systemd and desktop templates are Linux-only. On Windows, run &#039;&#039;&#039;webObsidian.bat&#039;&#039;&#039; manually or wrap it with your own service tooling.&lt;br /&gt;
&lt;br /&gt;
Installing these files does not enable or start anything. To enable, stop, reconfigure or remove the service, see [[Getting_Started#Running_Obsidian_as_a_Linux_user_service|Running Obsidian as a Linux user service]].&lt;br /&gt;
&lt;br /&gt;
== Obsidian Configuration ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Obsidian.png]]&lt;br /&gt;
&lt;br /&gt;
First, you must choose what [[Authenticator]] mechanism will be used within Obsidian. Obsidian supports five authentication methods: native (database), LDAP, OAuth (OIDC), SAML 2.0, and custom. Select &#039;&#039;Native (Database)&#039;&#039;, &#039;&#039;LDAP&#039;&#039;, &#039;&#039;OAuth (OIDC)&#039;&#039;, &#039;&#039;SAML 2.0&#039;&#039;, or &#039;&#039;Other&#039;&#039; (custom). &#039;&#039;Native (Database)&#039;&#039; requires no additional configuration and is what most users will select at least to start.&lt;br /&gt;
&lt;br /&gt;
Next, you&#039;ll want to select the email usage type. We highly recommend you configure Obsidian for email use as it will allow you to benefit from the event [[Event_Notifications|notification]] and [[Admin_Notifications|subscription]] support in Obsidian.&lt;br /&gt;
&lt;br /&gt;
Then you&#039;ll configure the log file location, license key (optional) and registered company name (optional). Registered company name is only required for our Site License users.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== OAuth / OIDC Configuration ====&lt;br /&gt;
&lt;br /&gt;
If you selected &#039;&#039;&#039;OAuth (OIDC)&#039;&#039;&#039; as the authentication type, the installer displays additional panels for OAuth configuration.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Provider and Common Settings =====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-OAuth_OIDC.png]]&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Identity Provider&#039;&#039;&#039; — Select your provider: Keycloak, Microsoft Entra ID, Okta, Auth0, or Generic OIDC.&lt;br /&gt;
* &#039;&#039;&#039;SSO Button label&#039;&#039;&#039; — Label shown on the &amp;quot;Sign in with…&amp;quot; button (e.g. &#039;&#039;Keycloak&#039;&#039;, &#039;&#039;Acme Corp SSO&#039;&#039;). If left blank the button reads &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
* &#039;&#039;&#039;Redirect URI&#039;&#039;&#039; — The callback URL Obsidian receives after IdP login. Must be registered at the IdP (e.g. &#039;&#039;https://app.example.com/oauth/callback&#039;&#039;).&lt;br /&gt;
* &#039;&#039;&#039;Logout mode&#039;&#039;&#039; — &#039;&#039;local&#039;&#039; (default) or &#039;&#039;rp&#039;&#039; (RP-initiated logout via the IdP&#039;s end_session_endpoint).&lt;br /&gt;
&lt;br /&gt;
===== Provider-Specific Settings =====&lt;br /&gt;
&lt;br /&gt;
Fill in the connection details for your chosen provider.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Keycloak.png]]&lt;br /&gt;
&lt;br /&gt;
;Keycloak&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — e.g. &#039;&#039;https://keycloak.example.com/realms/myrealm&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from the Keycloak client settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — recommended: &#039;&#039;openid profile email groups&#039;&#039;. &lt;br /&gt;
Note: Keycloak does not include a &#039;&#039;groups&#039;&#039; claim by default — a Group Membership mapper must be configured in the client&#039;s Mappers tab.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Microsoft-Entra-ID.png]]&lt;br /&gt;
&lt;br /&gt;
;Microsoft Entra ID&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — &#039;&#039;https://login.microsoftonline.com/&amp;lt;tenant-id&amp;gt;/v2.0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; — the Application (client) ID from the Azure portal.&lt;br /&gt;
* &#039;&#039;&#039;Client Secret&#039;&#039;&#039; — a client secret value from the Azure portal.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email&#039;&#039;. &lt;br /&gt;
Note: the &#039;&#039;groups&#039;&#039; claim contains GUIDs by default; configure Optional Claims in the Azure portal to receive group display names. &lt;br /&gt;
&#039;&#039;&#039;Multi-tenant applications are not supported in Obsidian 7.0.0.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Okta.png]]&lt;br /&gt;
&lt;br /&gt;
;Okta&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — use the &#039;&#039;&#039;Custom Authorization Server&#039;&#039;&#039; URL, e.g. &#039;&#039;https://&amp;lt;domain&amp;gt;.okta.com/oauth2/default&#039;&#039;. Do &#039;&#039;&#039;not&#039;&#039;&#039; use the Org Authorization Server URL.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from Okta application settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email groups&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Auth0.png]]&lt;br /&gt;
&lt;br /&gt;
;Auth0&lt;br /&gt;
* &#039;&#039;&#039;Domain / Issuer URL&#039;&#039;&#039; — e.g. &#039;&#039;https://&amp;lt;domain&amp;gt;.auth0.com/&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from Auth0 application settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email&#039;&#039;.&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; Auth0 issues opaque access tokens by default which are &#039;&#039;&#039;not supported&#039;&#039;&#039; on Obsidian&#039;s REST/Bearer path — configure a custom API in Auth0 to get JWT access tokens.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Generic_OIDC.png]]&lt;br /&gt;
&lt;br /&gt;
;Generic OIDC&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — the base URL; Obsidian appends &#039;&#039;/.well-known/openid-configuration&#039;&#039; to discover endpoints.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from the provider.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — at minimum &#039;&#039;openid&#039;&#039;; add &#039;&#039;profile&#039;&#039; and your groups scope as needed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Group → Role Mapping =====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-OAuth-Group-Role-Mapping.png]]&lt;br /&gt;
&lt;br /&gt;
The final OAuth panel maps IdP group values to Obsidian roles. &#039;&#039;&#039;Require role assignment&#039;&#039;&#039; (default yes) — when enabled, users with no matching roles cannot complete browser login. At least one mapping to the &#039;&#039;&#039;Admin&#039;&#039;&#039; role is required. The installer supports up to 10 group→role pairs; additional pairs can be added directly to the configuration file after install. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties]] for the full property reference.&lt;br /&gt;
&lt;br /&gt;
===== Registering the redirect URI at the IdP =====&lt;br /&gt;
&lt;br /&gt;
Before starting Obsidian, register the redirect URI shown in the installer at your IdP: Keycloak — Client → Settings → Valid Redirect URIs; Entra ID — App Registration → Authentication → Redirect URIs; Okta — Application → General Settings → Sign-in Redirect URIs; Auth0 — Application → Settings → Allowed Callback URLs. The URI must match &#039;&#039;com.carfey.suite.security.OAuthAuthenticator.redirectUri&#039;&#039; exactly, including scheme and port.&lt;br /&gt;
&lt;br /&gt;
==== LDAP Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.LDAP.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve selected LDAP Authentication, this screen takes you through configuring the server address and the various elements used to grant access. You should familiarize yourself with Obsidian [[Authenticator#Roles|Roles]]. The &#039;&#039;Access DN&#039;&#039; configuration element grants Read access to Obsidian application. You may use the same DN for more than one Role should you so wish.&lt;br /&gt;
&lt;br /&gt;
==== Custom Authenticator Configuration====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.CustomAuthenticator.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have chosen to use your own authentication mechanism, you must enter the fully qualified classname here.&lt;br /&gt;
&lt;br /&gt;
==== Database Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Database.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Next comes database configuration. If you are using JNDI, leave the username and password fields blank. If using JDBC URL, username and password fields are required.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;Database connections per instance&#039;&#039; and &#039;&#039;Database connection timeout(millis)&#039;&#039; fields are required and provide directive to the connection pool.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Database table name prefix&#039;&#039; is optional and is typically used when Obsidian will be colocated in an existing database/schema.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Database schema (Oracle/PostgreSQL)&#039;&#039; - This is used to allow for an alternate schema other than the default user&#039;s schema. We also recommend setting this value with Oracle/PostgreSQL when using JNDI as it allows more efficient database metadata loading.&lt;br /&gt;
&lt;br /&gt;
By default, the Obsidian installation will only include the JDBC libraries necessary for your particular database. At times you may wish to include the others to be able to change between databases. In those cases, check &#039;&#039;Include all supported JDBC libs&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Email Configuration ====&lt;br /&gt;
&lt;br /&gt;
If not using JNDI for mail sessions, provide the server and authentication details as required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.SSL.Email.png]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.TLS.Email.png]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.Open.Email.png]]&lt;br /&gt;
&lt;br /&gt;
==== JNDI Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.JNDI.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have selected JNDI for mail sessions, provide the JNDI path here. If you are using JNDI for Database connections, specify the database type.&lt;br /&gt;
&lt;br /&gt;
==== Configuring 3rd Party Library Conflict Management ====&lt;br /&gt;
[[Image:JarJar-Obsidian-6.0.0.png]]&lt;br /&gt;
&lt;br /&gt;
The Obsidian installer allows you to use [https://code.google.com/archive/p/jarjar JarJar] to handle potential conflicts between Obsidian&#039;s use of 3rd party libraries and versions used within your application. On this screen, simply select which libraries to which you wish to apply the JarJar bytecode modification process. This is optional and is skipped if no libraries are selected. Any jars not listed for which you are using more recent versions can be used and are therefore not included as options in the JarJar processing.&lt;br /&gt;
&lt;br /&gt;
==== Selecting Script Libraries ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-6.0.0-Choose-Scripting-Libraries.png]]&lt;br /&gt;
&lt;br /&gt;
The Obsidian installer allows you to select which script libraries you wish to include. Jython and JRuby are incompatible with each other, so the installer will only allow one of the two selected. If all script libraries are deselected, the only script jobs that can be run in Obsidian will be Javascript jobs as that engine is built into Java.&lt;br /&gt;
&lt;br /&gt;
==== Choosing Email Support ====&lt;br /&gt;
As of Obsidian 5.2.0, you can choose between JavaMail (javax) and Jakarta mail (Jakarta EE) implementations. When choosing Jakarta, you can also choose to bundle the Angus Jakarta compatible implementation. &lt;br /&gt;
&lt;br /&gt;
If you are using JNDI, this will only include the relevant support in the Obsidian WAR artifacts and the actual libraries in use must be provided by you and bundled with your container. If the standalone scheduler is selected along with JNDI, JNDI is assumed only relevant for WARs. As such, default libraries for either JavaMail or Jakarta will be included in the standalone installation.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.EmailImplementation.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
==== Additional configuration items ====&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 5.5.0, if you require any additional configuration items such as additional appenders/loggers or event hook configurations, you can add them here.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.5.0.Extra.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
==== Offline or restricted-network install ====&lt;br /&gt;
&lt;br /&gt;
Obsidian 7.0.0 resolves third-party libraries when you run the installer. For environments without direct access to Maven Central:&lt;br /&gt;
&lt;br /&gt;
# Pre-stage a local dependency cache containing every coordinate listed in the Obsidian dependency inventory for your release (contact Carfey support or your account team for the checklist file matching &#039;&#039;&#039;Obsidian-Install-7.0.0.jar&#039;&#039;&#039;).&lt;br /&gt;
# Copy the cache to the install host. Two layouts are supported:&lt;br /&gt;
#* &#039;&#039;&#039;Flat:&#039;&#039;&#039; one file per installed JAR name directly under the cache root.&lt;br /&gt;
#* &#039;&#039;&#039;File Maven repo:&#039;&#039;&#039; &#039;&#039;&#039;&amp;lt;group&amp;gt;/&amp;lt;artifact&amp;gt;/&amp;lt;version&amp;gt;/&amp;lt;installed-filename&amp;gt;.jar&#039;&#039;&#039;&lt;br /&gt;
# Run the installer with offline mode and the cache directory:&lt;br /&gt;
&lt;br /&gt;
 java -Dobsidian.install.deps.offline=true -Dobsidian.install.deps.cache.dir=/path/to/cache -jar Obsidian-Install-7.0.0.jar&lt;br /&gt;
&lt;br /&gt;
If a required library is missing from the cache, the install stops with an error naming the coordinate — add that artifact to the cache and re-run.&lt;br /&gt;
&lt;br /&gt;
===== Corporate Maven mirror (online) =====&lt;br /&gt;
&lt;br /&gt;
When your site mirrors Maven Central, point the installer at your repository base URL:&lt;br /&gt;
&lt;br /&gt;
 java -Dobsidian.install.deps.repo.url=https://nexus.example.com/repository/maven-public/ -jar Obsidian-Install-7.0.0.jar&lt;br /&gt;
&lt;br /&gt;
The installer reuses resolved artifacts for the duration of one install session.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Completing the Installation ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Finished.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Once you&#039;ve completed the Installation and Configuration screens, fully configured Obsidian Scheduler artifacts are now ready for you to use in the installation path you selected.&lt;br /&gt;
&lt;br /&gt;
Should you wish to automate future installations with the same configuration, click &#039;&#039;Generate an automatic installation script&#039;&#039;. This will prompt you to save an XML file that can be used for future installations using the automated install procedure &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar my-obsidian-configuration.xml&amp;lt;/code&amp;gt;. Every effort is made to ensure compatibility of these automated install files between versions. Any incompatibility will be noted in the [[Release_Notes|Release Notes]]. These automated installer files can also be used as templates for other environments, modifying them as necessary.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Installation_Guide&amp;diff=4165</id>
		<title>Installation Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Installation_Guide&amp;diff=4165"/>
		<updated>2026-07-31T15:30:53Z</updated>

		<summary type="html">&lt;p&gt;Craig: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;This installation guide generally applies to Obsidian versions 4.0 and newer but is specific to 7.0. You may see a few minor differences in 4.x, 5.x and 6.x releases.&lt;br /&gt;
&lt;br /&gt;
This installation guide is a companion to the Obsidian Installer UI. Its purpose is to provide additional detail as to the meaning of various inputs and to provide guidance on advanced usage of the installer.&lt;br /&gt;
&lt;br /&gt;
The installer itself installs and configures the artifacts. The configured artifacts will have their [[Advanced_Configuration#Properties_File|Properties]] file configured according to the choices made during the installation process. You may always change these later or use one of the [[Advanced_Configuration#Properties_File|override]] mechanisms.&lt;br /&gt;
== Running the Obsidian Installer ==&lt;br /&gt;
The installer is an executable JAR file in the Obsidian download zip package available on our [https://web.obsidianscheduler.com/download/ download page].&lt;br /&gt;
&lt;br /&gt;
The installer can be run from the command line as a graphical user interface using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar&amp;lt;/code&amp;gt; or in interactive console mode using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar -console&amp;lt;/code&amp;gt;. Note that you will have to replace the JAR file name with the actual versioned name in your installation.&lt;br /&gt;
&lt;br /&gt;
On some platforms, simple double-clicking the JAR file will start it in graphical interface mode.&lt;br /&gt;
&lt;br /&gt;
If you are doing a version upgrade of Obsidian or are otherwise uninterested in actually configuring the artifacts, you can run the quick start mode to get the default configured artifacts using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar h2-tomcat-quick-start.xml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Obsidian Installer Artifacts ==&lt;br /&gt;
&lt;br /&gt;
The Obsidian Installer installs and configures a number of artifacts. You can choose which artifacts to create, but most users can leave the default options selected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Installation-Packages.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Obsidian WAR&#039;&#039;&#039; - This is the Obsidian Web Admin UI with scheduler component enabled. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/obsidian.war!/WEB-INF/classes/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Standalone Admin WAR&#039;&#039;&#039; - This is the Obsidian Web Admin UI with scheduler component disabled. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/standaloneObsidianAdmin.war!/WEB-INF/classes/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Standalone Obsidian Runtime&#039;&#039;&#039; - This is a runtime folder containing the libraries and configuration necessary for running the Obsidian Scheduler component either as a standalone module or for use as an Embedded Scheduler in your application. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/standalone/obsidian-props.jar!/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian License Proxy&#039;&#039;&#039; - Allows for local license leasing from a [[Key_Server_Proxy|Key Server Proxy]] that ultimately leases licenses from the Obsidian License server.&lt;br /&gt;
# &#039;&#039;&#039;Documentation&#039;&#039;&#039; - README and Embedded API Javadoc.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; — An embedded Apache Tomcat runtime for running Obsidian locally without an external servlet container. Requires the Obsidian WAR pack.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; — An embedded Apache Tomcat runtime for running the standalone admin WAR locally. Requires the Obsidian Standalone Admin WAR pack.&lt;br /&gt;
# &#039;&#039;&#039;License&#039;&#039;&#039; - Obsidian and 3rd party license information.&lt;br /&gt;
&lt;br /&gt;
==== Deployment options ====&lt;br /&gt;
&lt;br /&gt;
Obsidian 7.0.0 offers four deployment profiles in the installer:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Obsidian WAR&#039;&#039;&#039; — deploy &#039;&#039;&#039;obsidian.war&#039;&#039;&#039; to your own servlet container (Tomcat, WebLogic, etc.).&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; — adds an embedded Tomcat runtime and &#039;&#039;&#039;webObsidian&#039;&#039;&#039; scripts to run &#039;&#039;&#039;obsidian.war&#039;&#039;&#039; locally (&#039;&#039;&#039;start scheduler&#039;&#039;&#039;). &#039;&#039;&#039;Requires&#039;&#039;&#039; the Obsidian WAR pack.&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Standalone Admin WAR&#039;&#039;&#039; — admin console WAR for an external servlet container.&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; — embedded runtime for the admin WAR (&#039;&#039;&#039;start adminOnly&#039;&#039;&#039;). &#039;&#039;&#039;Requires&#039;&#039;&#039; the Standalone Admin WAR pack.&lt;br /&gt;
&lt;br /&gt;
Embedded Tomcat does &#039;&#039;&#039;not&#039;&#039;&#039; replace the WAR on disk — it runs the installed WAR via the helper. Do not select an embed pack without its matching WAR pack. For how to start, stop and change the ports of the embedded runtime, see [[Getting_Started#Starting_Obsidian_with_embedded_Tomcat|Starting Obsidian with embedded Tomcat]].&lt;br /&gt;
&lt;br /&gt;
==== Linux desktop and systemd integration ====&lt;br /&gt;
&lt;br /&gt;
On a Unix host, an embedded Tomcat pack also copies a &#039;&#039;&#039;linux/&#039;&#039;&#039; directory under your install path holding user-level systemd and desktop-entry templates, and the installer offers to activate them. Nothing is installed system-wide and no root privileges are required.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Embedded Tomcat - Linux integration (optional)&#039;&#039;&#039; panel carries the checkbox &#039;&#039;&#039;Install Linux desktop and user systemd integration&#039;&#039;&#039; (unchecked by default). Tick it to have the installer run the integration script for you; the option only takes effect if you also selected an embedded Tomcat pack. Leaving it unchecked still copies the templates, so you can opt in later by running this from the install directory:&lt;br /&gt;
&lt;br /&gt;
 ./linux/install-embedded-linux-integration.sh &amp;quot;$PWD&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Either route writes two files, for the installing user only:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/.config/systemd/user/obsidian-embedded@scheduler.service&#039;&#039;&lt;br /&gt;
* &#039;&#039;~/.local/share/applications/obsidian-embedded.desktop&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;XDG_CONFIG_HOME&#039;&#039; and &#039;&#039;XDG_DATA_HOME&#039;&#039; are honoured when set.)&lt;br /&gt;
&lt;br /&gt;
For an automated (headless) install, request the integration with:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;entry key=&amp;quot;embed.linux.integration&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This panel is &#039;&#039;&#039;not shown on Windows installs&#039;&#039;&#039; — the systemd and desktop templates are Linux-only. On Windows, run &#039;&#039;&#039;webObsidian.bat&#039;&#039;&#039; manually or wrap it with your own service tooling.&lt;br /&gt;
&lt;br /&gt;
Installing these files does not enable or start anything. To enable, stop, reconfigure or remove the service, see [[Getting_Started#Running_Obsidian_as_a_Linux_user_service|Running Obsidian as a Linux user service]].&lt;br /&gt;
&lt;br /&gt;
== Obsidian Configuration ==&lt;br /&gt;
&lt;br /&gt;
First, you must choose what [[Authenticator]] mechanism will be used within Obsidian. Select &#039;&#039;Native (Database)&#039;&#039;, &#039;&#039;LDAP&#039;&#039;, &#039;&#039;OAuth (OIDC)&#039;&#039;, &#039;&#039;SAML 2.0&#039;&#039;, or &#039;&#039;Other&#039;&#039; (custom). &#039;&#039;Native (Database)&#039;&#039; requires no additional configuration and is what most users will select at least to start.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Obsidian.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
First, you must choose what [[Authenticator]] mechanism will be used within Obsidian. &#039;&#039;Native (Database)&#039;&#039; authentication requires no additional configuration and is what most users will select. Select &#039;&#039;Other&#039;&#039; if you have implemented your own authentication mechanism, OAuth (OIDC), SAML or LDAP according to your desired mechanism. &lt;br /&gt;
&lt;br /&gt;
Next, you&#039;ll want to select the email usage type. We highly recommend you configure Obsidian for email use as it will allow you to benefit from the event [[Event_Notifications|notification]] and [[Admin_Notifications|subscription]] support in Obsidian.&lt;br /&gt;
&lt;br /&gt;
Then you&#039;ll configure the log file location, license key (optional) and registered company name (optional). Registered company name is only required for our Site License users.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== OAuth / OIDC Configuration ====&lt;br /&gt;
&lt;br /&gt;
If you selected &#039;&#039;&#039;OAuth (OIDC)&#039;&#039;&#039; as the authentication type, the installer displays additional panels for OAuth configuration.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Provider and Common Settings =====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-OAuth_OIDC.png]]&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Identity Provider&#039;&#039;&#039; — Select your provider: Keycloak, Microsoft Entra ID, Okta, Auth0, or Generic OIDC.&lt;br /&gt;
* &#039;&#039;&#039;SSO Button label&#039;&#039;&#039; — Label shown on the &amp;quot;Sign in with…&amp;quot; button (e.g. &#039;&#039;Keycloak&#039;&#039;, &#039;&#039;Acme Corp SSO&#039;&#039;). If left blank the button reads &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
* &#039;&#039;&#039;Redirect URI&#039;&#039;&#039; — The callback URL Obsidian receives after IdP login. Must be registered at the IdP (e.g. &#039;&#039;https://app.example.com/oauth/callback&#039;&#039;).&lt;br /&gt;
* &#039;&#039;&#039;Logout mode&#039;&#039;&#039; — &#039;&#039;local&#039;&#039; (default) or &#039;&#039;rp&#039;&#039; (RP-initiated logout via the IdP&#039;s end_session_endpoint).&lt;br /&gt;
&lt;br /&gt;
===== Provider-Specific Settings =====&lt;br /&gt;
&lt;br /&gt;
Fill in the connection details for your chosen provider.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Keycloak.png]]&lt;br /&gt;
&lt;br /&gt;
;Keycloak&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — e.g. &#039;&#039;https://keycloak.example.com/realms/myrealm&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from the Keycloak client settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — recommended: &#039;&#039;openid profile email groups&#039;&#039;. &lt;br /&gt;
Note: Keycloak does not include a &#039;&#039;groups&#039;&#039; claim by default — a Group Membership mapper must be configured in the client&#039;s Mappers tab.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Microsoft-Entra-ID.png]]&lt;br /&gt;
&lt;br /&gt;
;Microsoft Entra ID&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — &#039;&#039;https://login.microsoftonline.com/&amp;lt;tenant-id&amp;gt;/v2.0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; — the Application (client) ID from the Azure portal.&lt;br /&gt;
* &#039;&#039;&#039;Client Secret&#039;&#039;&#039; — a client secret value from the Azure portal.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email&#039;&#039;. &lt;br /&gt;
Note: the &#039;&#039;groups&#039;&#039; claim contains GUIDs by default; configure Optional Claims in the Azure portal to receive group display names. &lt;br /&gt;
&#039;&#039;&#039;Multi-tenant applications are not supported in Obsidian 7.0.0.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Okta.png]]&lt;br /&gt;
&lt;br /&gt;
;Okta&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — use the &#039;&#039;&#039;Custom Authorization Server&#039;&#039;&#039; URL, e.g. &#039;&#039;https://&amp;lt;domain&amp;gt;.okta.com/oauth2/default&#039;&#039;. Do &#039;&#039;&#039;not&#039;&#039;&#039; use the Org Authorization Server URL.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from Okta application settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email groups&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Auth0.png]]&lt;br /&gt;
&lt;br /&gt;
;Auth0&lt;br /&gt;
* &#039;&#039;&#039;Domain / Issuer URL&#039;&#039;&#039; — e.g. &#039;&#039;https://&amp;lt;domain&amp;gt;.auth0.com/&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from Auth0 application settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email&#039;&#039;.&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; Auth0 issues opaque access tokens by default which are &#039;&#039;&#039;not supported&#039;&#039;&#039; on Obsidian&#039;s REST/Bearer path — configure a custom API in Auth0 to get JWT access tokens.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Generic_OIDC.png]]&lt;br /&gt;
&lt;br /&gt;
;Generic OIDC&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — the base URL; Obsidian appends &#039;&#039;/.well-known/openid-configuration&#039;&#039; to discover endpoints.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from the provider.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — at minimum &#039;&#039;openid&#039;&#039;; add &#039;&#039;profile&#039;&#039; and your groups scope as needed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Group → Role Mapping =====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-OAuth-Group-Role-Mapping.png]]&lt;br /&gt;
&lt;br /&gt;
The final OAuth panel maps IdP group values to Obsidian roles. &#039;&#039;&#039;Require role assignment&#039;&#039;&#039; (default yes) — when enabled, users with no matching roles cannot complete browser login. At least one mapping to the &#039;&#039;&#039;Admin&#039;&#039;&#039; role is required. The installer supports up to 10 group→role pairs; additional pairs can be added directly to the configuration file after install. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties]] for the full property reference.&lt;br /&gt;
&lt;br /&gt;
===== Registering the redirect URI at the IdP =====&lt;br /&gt;
&lt;br /&gt;
Before starting Obsidian, register the redirect URI shown in the installer at your IdP: Keycloak — Client → Settings → Valid Redirect URIs; Entra ID — App Registration → Authentication → Redirect URIs; Okta — Application → General Settings → Sign-in Redirect URIs; Auth0 — Application → Settings → Allowed Callback URLs. The URI must match &#039;&#039;com.carfey.suite.security.OAuthAuthenticator.redirectUri&#039;&#039; exactly, including scheme and port.&lt;br /&gt;
&lt;br /&gt;
==== LDAP Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.LDAP.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve selected LDAP Authentication, this screen takes you through configuring the server address and the various elements used to grant access. You should familiarize yourself with Obsidian [[Authenticator#Roles|Roles]]. The &#039;&#039;Access DN&#039;&#039; configuration element grants Read access to Obsidian application. You may use the same DN for more than one Role should you so wish.&lt;br /&gt;
&lt;br /&gt;
==== Custom Authenticator Configuration====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.CustomAuthenticator.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have chosen to use your own authentication mechanism, you must enter the fully qualified classname here.&lt;br /&gt;
&lt;br /&gt;
==== Database Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Database.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Next comes database configuration. If you are using JNDI, leave the username and password fields blank. If using JDBC URL, username and password fields are required.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;Database connections per instance&#039;&#039; and &#039;&#039;Database connection timeout(millis)&#039;&#039; fields are required and provide directive to the connection pool.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Database table name prefix&#039;&#039; is optional and is typically used when Obsidian will be colocated in an existing database/schema.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Database schema (Oracle/PostgreSQL)&#039;&#039; - This is used to allow for an alternate schema other than the default user&#039;s schema. We also recommend setting this value with Oracle/PostgreSQL when using JNDI as it allows more efficient database metadata loading.&lt;br /&gt;
&lt;br /&gt;
By default, the Obsidian installation will only include the JDBC libraries necessary for your particular database. At times you may wish to include the others to be able to change between databases. In those cases, check &#039;&#039;Include all supported JDBC libs&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Email Configuration ====&lt;br /&gt;
&lt;br /&gt;
If not using JNDI for mail sessions, provide the server and authentication details as required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.SSL.Email.png]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.TLS.Email.png]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.Open.Email.png]]&lt;br /&gt;
&lt;br /&gt;
==== JNDI Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.JNDI.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have selected JNDI for mail sessions, provide the JNDI path here. If you are using JNDI for Database connections, specify the database type.&lt;br /&gt;
&lt;br /&gt;
==== Configuring 3rd Party Library Conflict Management ====&lt;br /&gt;
[[Image:JarJar-Obsidian-6.0.0.png]]&lt;br /&gt;
&lt;br /&gt;
The Obsidian installer allows you to use [https://code.google.com/archive/p/jarjar JarJar] to handle potential conflicts between Obsidian&#039;s use of 3rd party libraries and versions used within your application. On this screen, simply select which libraries to which you wish to apply the JarJar bytecode modification process. This is optional and is skipped if no libraries are selected. Any jars not listed for which you are using more recent versions can be used and are therefore not included as options in the JarJar processing.&lt;br /&gt;
&lt;br /&gt;
==== Selecting Script Libraries ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-6.0.0-Choose-Scripting-Libraries.png]]&lt;br /&gt;
&lt;br /&gt;
The Obsidian installer allows you to select which script libraries you wish to include. Jython and JRuby are incompatible with each other, so the installer will only allow one of the two selected. If all script libraries are deselected, the only script jobs that can be run in Obsidian will be Javascript jobs as that engine is built into Java.&lt;br /&gt;
&lt;br /&gt;
==== Choosing Email Support ====&lt;br /&gt;
As of Obsidian 5.2.0, you can choose between JavaMail (javax) and Jakarta mail (Jakarta EE) implementations. When choosing Jakarta, you can also choose to bundle the Angus Jakarta compatible implementation. &lt;br /&gt;
&lt;br /&gt;
If you are using JNDI, this will only include the relevant support in the Obsidian WAR artifacts and the actual libraries in use must be provided by you and bundled with your container. If the standalone scheduler is selected along with JNDI, JNDI is assumed only relevant for WARs. As such, default libraries for either JavaMail or Jakarta will be included in the standalone installation.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.EmailImplementation.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
==== Additional configuration items ====&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 5.5.0, if you require any additional configuration items such as additional appenders/loggers or event hook configurations, you can add them here.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.5.0.Extra.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
==== Offline or restricted-network install ====&lt;br /&gt;
&lt;br /&gt;
Obsidian 7.0.0 resolves third-party libraries when you run the installer. For environments without direct access to Maven Central:&lt;br /&gt;
&lt;br /&gt;
# Pre-stage a local dependency cache containing every coordinate listed in the Obsidian dependency inventory for your release (contact Carfey support or your account team for the checklist file matching &#039;&#039;&#039;Obsidian-Install-7.0.0.jar&#039;&#039;&#039;).&lt;br /&gt;
# Copy the cache to the install host. Two layouts are supported:&lt;br /&gt;
#* &#039;&#039;&#039;Flat:&#039;&#039;&#039; one file per installed JAR name directly under the cache root.&lt;br /&gt;
#* &#039;&#039;&#039;File Maven repo:&#039;&#039;&#039; &#039;&#039;&#039;&amp;lt;group&amp;gt;/&amp;lt;artifact&amp;gt;/&amp;lt;version&amp;gt;/&amp;lt;installed-filename&amp;gt;.jar&#039;&#039;&#039;&lt;br /&gt;
# Run the installer with offline mode and the cache directory:&lt;br /&gt;
&lt;br /&gt;
 java -Dobsidian.install.deps.offline=true -Dobsidian.install.deps.cache.dir=/path/to/cache -jar Obsidian-Install-7.0.0.jar&lt;br /&gt;
&lt;br /&gt;
If a required library is missing from the cache, the install stops with an error naming the coordinate — add that artifact to the cache and re-run.&lt;br /&gt;
&lt;br /&gt;
===== Corporate Maven mirror (online) =====&lt;br /&gt;
&lt;br /&gt;
When your site mirrors Maven Central, point the installer at your repository base URL:&lt;br /&gt;
&lt;br /&gt;
 java -Dobsidian.install.deps.repo.url=https://nexus.example.com/repository/maven-public/ -jar Obsidian-Install-7.0.0.jar&lt;br /&gt;
&lt;br /&gt;
The installer reuses resolved artifacts for the duration of one install session.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Completing the Installation ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Finished.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Once you&#039;ve completed the Installation and Configuration screens, fully configured Obsidian Scheduler artifacts are now ready for you to use in the installation path you selected.&lt;br /&gt;
&lt;br /&gt;
Should you wish to automate future installations with the same configuration, click &#039;&#039;Generate an automatic installation script&#039;&#039;. This will prompt you to save an XML file that can be used for future installations using the automated install procedure &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar my-obsidian-configuration.xml&amp;lt;/code&amp;gt;. Every effort is made to ensure compatibility of these automated install files between versions. Any incompatibility will be noted in the [[Release_Notes|Release Notes]]. These automated installer files can also be used as templates for other environments, modifying them as necessary.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Release_Notes_-_Older_Releases&amp;diff=4164</id>
		<title>Release Notes - Older Releases</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Release_Notes_-_Older_Releases&amp;diff=4164"/>
		<updated>2026-07-31T15:14:43Z</updated>

		<summary type="html">&lt;p&gt;Craig: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;toclimit-2&amp;quot;&amp;gt;__TOC__&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.10.6 == &lt;br /&gt;
Released April 2021.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
* Correct issue where PostgreSQL database connection validation exceptions are masked.&lt;br /&gt;
* Fix UI styling issues in Firefox browser.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.10.5 == &lt;br /&gt;
Released October 2020. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* New &#039;&#039;LW&#039;&#039; instruction for day-of-month field in [[Cron#Special_Character_Usage|Cron]] to indicate last weekday of month.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
* Fix for &#039;&#039;W&#039;&#039; day-of-month field in [[Cron#Special_Character_Usage|Cron]] skipping months where ordinal is last ordinal of the month and falls on the last Sunday of the month.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.10.4 == &lt;br /&gt;
Released July 2020. &lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
* Fix UTF-8 handling in [[REST_API|REST API]] calls.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.10.3 == &lt;br /&gt;
Released March 2020. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Introduce better handling for edge case where a long running job that loses connectivity to the Obsidian database gets marked as Died while it is still running and then eventually reconnects to the database and its heartbeat updates resume. Heartbeat updates will now be prevented and new special events are triggered when this case is detected and when the job finally terminates.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
* Fix JobIds parameter in [[Embedded_API#List_Job_Dashboard_.28Latest_Scheduled_Runtime_by_Job.29|Embedded]] API for Job Dashboard (Last Occurrence of Jobs)&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.10.2 == &lt;br /&gt;
Released October 2019. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* [[REST_Endpoints#GET_a_list_of_the_latest_scheduled_runtime_by_job_.28supports_multiple_jobs.29|REST]] and [[Embedded_API#List_Job_Dashboard_.28Latest_Scheduled_Runtime_by_Job.29|Embedded]] API for Job Dashboard (Last Occurrence of Jobs)&lt;br /&gt;
* Timezone now included when dates are in event messages.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.10.1 == &lt;br /&gt;
Released June 2019. &lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* [[Recovery_%26_Failover#Startup_.2F_Shutdown_Mode|Startup/Shutdown]] configuration of jobs not being applied in UI.&lt;br /&gt;
* [[Admin_Scheduler_Settings|Scheduler setting]] jobShutdownWaitTimeSeconds in Job category could not be set to -1 in the UI.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.10.0 == &lt;br /&gt;
Released April 2019. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Support for automated [[Recovery_%26_Failover#Startup_.2F_Shutdown_Mode|Startup/Shutdown]] invocation of jobs.&lt;br /&gt;
* [[Implementing_Jobs#Parameterization|Parameters]] now have optional support for validation of list values to be unique.&lt;br /&gt;
* Formalized support for [[Scripting_Jobs|Ruby Script jobs]] via JRuby.&lt;br /&gt;
* Enhanced [[Installation_Guide#Selecting_Script_Libraries|installer]] to allow selectivity of desired script libraries/jobs. &#039;&#039;&#039;Breaking change to automated installer files.&#039;&#039;&#039; &amp;lt;ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using automated installer files from previous releases, you will need to add a new section of xml as of Obsidian 4.10.0 to handle a new UserInputPanel. Immediately after the UserInputPanel.15 closing brace, add the following:&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;com.izforge.izpack.panels.UserInputPanel id=&amp;quot;UserInputPanel.16&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;userInput&amp;gt;&lt;br /&gt;
&amp;lt;entry key=&amp;quot;script.beanshell&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;entry key=&amp;quot;script.jruby&amp;quot; value=&amp;quot;false&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;entry key=&amp;quot;script.groovy&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;entry key=&amp;quot;script.jython&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/userInput&amp;gt;&lt;br /&gt;
&amp;lt;/com.izforge.izpack.panels.UserInputPanel&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can change the parameter values as desired. The above reflects the options for all versions prior to Obsidian 4.10.0.&lt;br /&gt;
&amp;lt;/ref&amp;gt;&lt;br /&gt;
* Formalized support for [[Getting_Started#Supported_Platforms|MS SQL 2016 &amp;amp; 2017]]&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.9.1 == &lt;br /&gt;
Released March 2019. &lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* ClasspathJobScanner fails on job classes with no [[Implementing_Jobs#SchedulableJob_Interface|@Description]] annotation&lt;br /&gt;
* In some cases, [[Built-in_Jobs#Job_History_Cleanup_Job|Job History Cleanup Job]] was not deleting certain history and related records.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.9.0 == &lt;br /&gt;
Released February 2019. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* [[Implementing_Jobs#SchedulableJob_Interface|Description]]/[[Implementing_Jobs#Parameterization|Parameter]] annotation support optional URLs arrays&lt;br /&gt;
* New built-in maintenance job [[Built-in_Jobs#Notification_Cleanup_Job|Delete Notifications Job]]&lt;br /&gt;
* New subscribable event for first occurrence of a host not running a job due to not being one of the fixed hosts.&lt;br /&gt;
* [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/Context.html Context] class expanded to include some additional helpful attributes.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* [[Admin_Jobs#Nickname.2C_Class_and_Folder|Folder]] Selection in UI fixed to allow intermediary folders selections&lt;br /&gt;
* [[Event_Hooks#Slack_Notifier|SlackEventHook]] fixed to include ERROR level&lt;br /&gt;
* Logout from My User page fixed to redirect to Login page&lt;br /&gt;
* Fixed an issue with PostgreSQL DDL that could report wrong exception on execution failures.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.8.0 == &lt;br /&gt;
Released November 2018. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Improve performance of job activity view when retrieving large job errors.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Corrected an issue where in some situations job conflict priority calculation was incorrect.&lt;br /&gt;
* Missed and Overlapped job events are now subscribable.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.7.1 == &lt;br /&gt;
Released October 2018. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* New LDAP [[Advanced_Configuration#Authentication_Properties|Configuration Parameter]] to support additional group membership attributes.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Correct Non-English Locale issues in database DDL/DML.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.7.0 == &lt;br /&gt;
Released September 2018. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Optimized [[Built-in_Jobs#Job_History_Cleanup_Job|JobHistoryCleanupJob]] for better performance and reduced locking&lt;br /&gt;
* New [[REST_Endpoints#DELETE_a_future_scheduled_runtime|REST]] and [[Embedded_API#Delete_a_Future_Scheduled_Runtime_for_a_Job|Embedded]] API function for targeted deletion of a single future one time submission including its parameters.&lt;br /&gt;
* Modified job_state index to improve job listing performance in cases where there are a large of future-dated [[Job_Features#Resubmission_.26_Ad_Hoc_Runs|ad-hoc submissions]].&lt;br /&gt;
* PostgreSQL 10 Support&lt;br /&gt;
* Oracle 12c support&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Add missing job_history index on PostgreSQL installations&lt;br /&gt;
* Corrected issue where Get Schedules [[Embedded_API#List_a_Job.27s_Schedules|Embedded]] and [[REST_Endpoints#GET_a_list_of_an_existing_job.27s_schedules|REST]] APIs were setting all parameters to the ordinal of the last parameter.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.6.2 == &lt;br /&gt;
Released August 2018. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Reduce locking in certain cases of job delete.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Corrected NPE when performing recovery on a job that has transitioned from Disabled to Enabled state.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.6.1 == &lt;br /&gt;
Released July 2018. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Performance improvements related to Job Result loading.&lt;br /&gt;
* Added informational logging for cases where licence key configuration will not applied.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Corrected issue where stale admin_only hosts were not cleaned from database.&lt;br /&gt;
* Corrected issue where licence release failures were never logged due to shutdown state.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.6.0 == &lt;br /&gt;
Released June 2018. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* [[Advanced_Configuration#Miscellaneous_Properties|Support]] for starting a scheduler node in paused state.&lt;br /&gt;
* Several performance improvements for [[Admin_Job_Activity|Job Activity]] view.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Removed class reference that was causing failures running Obsidian against Java 10.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.5.1 == &lt;br /&gt;
Released April 2018. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Upgrade of [[Advanced_Configuration#Dependent_Libraries|bundled Groovy library]] which addresses known Groovy memory leaks.&lt;br /&gt;
* [[Getting_Started#Standalone_Scheduler|Standalone mode]] now attempts graceful shut down when SIGTERM is issued.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* [[Authenticator#Customizing_our_LDAP_Authenticator|LDAPAuthenticator]] no longer fails when combining delimited base.dn with search user.&lt;br /&gt;
* Defensively protect against possible stack overflow in [[Spring_Integration#Dependency_Injection_via_Spring|SpringContextAware]] for cyclical annotation hierarchies.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.5.0 == &lt;br /&gt;
Released March 2018. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* [[Event_Hooks|Custom Event Hooks]] allow for custom triggers on Obsidian events for things such as instant messaging, customized logging, etc.&lt;br /&gt;
* [[Event_Hooks#Slack_Notifier|Slack Notifier]] implemented as an [[Event_Hooks|Event Hook]]&lt;br /&gt;
* [[Implementing_Jobs#Async_Jobs|Async job]] support&lt;br /&gt;
* [[Implementing_Jobs#Dynamic_List_Providers|Dynamic List Provider]] support&lt;br /&gt;
* LDAP [[Authenticator|Authenticator]] to support delimited value [[Advanced_Configuration#Authentication_Properties|configurations]] for dn.base and Obsidian role/group mapping.&lt;br /&gt;
* Directory/File classes now provide folder/file in question throwing underlying filesystem exceptions.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* [[Recovery_%26_Failover#Configurable_Job_Recovery|Last recovery]] cron patterns no longer fail crossing some boundaries&lt;br /&gt;
* Not all job attributes were being applied in Obsidian [[Initializing_and_Restoring#Startup_Initialization|initialization file]]. Pickup buffer minutes wasn&#039;t being used in both create and update cases, job folder wasn&#039;t being used in update cases.&lt;br /&gt;
* Correct invalid Oracle index name&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.4.1 == &lt;br /&gt;
Released January 2018. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Job Queuer, Spawn Locking, Last Recovery performance improvements, includes a new database index.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Admin UI restores previous search filter settings. In cases where the last job with a given Job Folder was deleted, or for screens that filter by Job Nickname, the filter was still applied but not visible resulting in no results returned. Now, any invalid options are automatically removed.&lt;br /&gt;
* [[Recovery_%26_Failover#Configurable_Job_Recovery|Last Recovery]] in rare cases would recover too far back.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.4.0 == &lt;br /&gt;
Released October 2017. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Fixed Hosts now support [[Admin_Jobs#Advanced_Options | Blacklists]]&lt;br /&gt;
* Job Results now support [[Implementing_Jobs#Customizing_Job_Result_Serialization_and_Deserialization | Custom Serialization/Deserialization]] and default Gson JSON for complex objects.&lt;br /&gt;
* [[Advanced_Configuration#Properties_File|Configuration]] values may be specified as System Properties/Environment Variables.&lt;br /&gt;
* Obsidian upgrades now support [[Getting_Started#Disabling_DDL_Updates|data-only upgrades]] (structural changes are pre-applied offline).&lt;br /&gt;
* Job classes may now be partially introduced into the cluster and subsequently scheduled/executed without being attempted on missing hosts avoiding failures (via JobSpawner parameter &#039;&#039;skipSpawnAttemptsForUnavailableJobs&#039;&#039;).&lt;br /&gt;
* [[Embedded API]] and [[REST API]]s now return basic audit values (created/updated user/time)&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Obsidian upgrades spanning multiple structural and data changes to a given table no longer fail.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.3.0 == &lt;br /&gt;
Released June 2017. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* [[REST_Endpoints#GET_licence_details_on_an_existing_scheduling_host |REST Endpoint]] and [[Embedded_API#Get_Licence_Health_by_Host|Embedded API]] for Licensing Health Check&lt;br /&gt;
* Support Markdown and customizable [[Description_%26_Parameter_Formatting|Formatting]] in Job description and Parameter description annotations&lt;br /&gt;
* Support timed wait as the new default on FOR UPDATE statements in Oracle - [[Advanced_Configuration#Database_Properties | Advanced Configuration - Database Properties]]. Old or customized behaviour possible via configuration properties.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fix issue in [[Built-in_Jobs#Disabled_Job_Cleanup_Job|DisabledJobCleanupJob]] that fails to delete disabled jobs with chained and/or resubmitted records.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.2.1 == &lt;br /&gt;
Released May 2017. &lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* [[Cron#Recurrence|Recurrence]] schedules in server recovery will fail if last runtime doesn&#039;t exist.&lt;br /&gt;
* A job that is configured against a job class that has since been removed from the cluster can now be [[Admin_Jobs|loaded]]/[[Admin_Jobs#Deleting|deleted]] from the UI.&lt;br /&gt;
* [[Admin_Jobs#Deleting|Delete Job]] - If the delete operation fails for any reason and the operator leaves the modal and returns, the error from the previous operation was still visible.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.2.0 == &lt;br /&gt;
Released March 2017. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Time Zone support in [[Cron|Cron]] patterns. Allows a cluster running in a single time zone to run jobs designated in different timezones.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* LAST [[Recovery_%26_Failover#Configurable_Job_Recovery|recovery mode]] wouldn&#039;t fire latest job on cluster recovery if the job had never fired or if the last runtime had been deleted by the maintenance job.&lt;br /&gt;
* ALL [[Recovery_%26_Failover#Configurable_Job_Recovery|recovery mode]] would fire first missed job, but the remaining ones wouldn&#039;t run and would be set as OVERLAPPED.&lt;br /&gt;
* When creating a new schedule that overlaps one or more previously configured future runtimes with parameters, parameters are now deleted from the database.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.1.0 == &lt;br /&gt;
Released February 2017. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* [[Admin_Jobs#Nickname.2C_Class_and_Folder|Folder]]/tag support for jobs including a [[REST_Endpoints#GET_a_list_of_job_folders|REST]] endpoint and [[Embedded_API#List_Job_Folders|Embedded]] method.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* UI issue in Job Execution Subscriptions and Templates - selected jobs now displaying correctly after save/refresh.&lt;br /&gt;
* [[Initializing_and_Restoring#Transfer_or_Restore_a_Configuration|UpdateConfigurationAction]] no longer fails in non-native Authentication mode if an empty Users element is provided.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.0.2 == &lt;br /&gt;
Released October 2016. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* [[Implementing_Jobs#Parameterization|Job Parameter]]s now support an optional &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; to be displayed in UI Help.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fixed an issue where [[Cron#Special_Character_Usage|Cron special character]] &amp;lt;code&amp;gt;L&amp;lt;/code&amp;gt; would not work if specified as the only value in a field.&lt;br /&gt;
* Fixed 3.8.0 MySQL upgrade script.&lt;br /&gt;
* Fixed an issue where Obsidian continued to attempt regular licence verification after a valid site licence had been configured.&lt;br /&gt;
* Fixed an issue where table prefixes weren&#039;t used in all lookups.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.0.1 == &lt;br /&gt;
Released August 2016. &lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fixed bug where a chained job that became conflicted would not evaluate chain conditions once it was no longer conflicted.&lt;br /&gt;
* Fixed catalina.base bug and dos line endings in jetty.sh script in bundled jetty server.&lt;br /&gt;
* Fixed job parameter edit box resizing bug.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.0.0 == &lt;br /&gt;
Released July 2016. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* All New User Interface&lt;br /&gt;
* Obsidian is now built using [[Getting_Started#Supported_Platforms|Java 7]]. No longer supports Java 6.&lt;br /&gt;
* [[Getting_Started#Really_Quick_Start_With_H2_and_Jetty|Quick Start]] now uses Jetty.&lt;br /&gt;
* Many [[Advanced_Configuration#Dependent_Libraries|updated libraries]] including some that had been updated to Java 7. &lt;br /&gt;
** &#039;&#039;Breaking Change&#039;&#039; New H2 library. If you&#039;re using the H2 database, you will want to look into [[Initializing_and_Restoring|exporting and restoring]] your Obsidian environment&#039;s configuration.&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* [[REST_Endpoints#GET_a_system_restore_configuration|GetConfiguration]] filtering no longer fails when Templates/Subscriptions are job targeted.&lt;br /&gt;
&lt;br /&gt;
== All Other Releases ==&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.8.0 === &lt;br /&gt;
Released June 2016. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* [[Job_Features#Resubmission_.26_Ad_Hoc_Runs|Ad Hoc]] runs now may be submitted multiple times per minute. [[Chaining|Chains]] may result in target jobs multiple times per minute. Existing behaviour of [[Admin_Jobs#Execution_.26_Pickup|Chain All]] job configuration remains unchanged.&lt;br /&gt;
* [[Built-in_Jobs#Script_Job|Script Job]] now supports best-effort interruption.&lt;br /&gt;
* APIs for GET System Restore Configuration support filtering for both [[REST_Endpoints#GET_a_system_restore_configuration|REST]] and [[Embedded_API#Get_System_Restore_Configuration|Embedded]].&lt;br /&gt;
* [[Implementing_Jobs#Parameterization|Context]] for job execution now supports retrieving the chain source job config, when applicable. See also the Javadoc for [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/Context.html#getSourceConfig() Context].&lt;br /&gt;
* [[Getting_Started#Setting_Host_Names|Host names]] maximum length increased to 255 characters from 50.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fixed an issue where [[Admin_Jobs#Execution_.26_Pickup|auto-interrupts]] were not firing.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.7.1 === &lt;br /&gt;
Released April 2016. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Job APIs windows now with improved locking/blocking on schedule modifications.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* [[REST_Endpoints#GET_a_system_restore_configuration|GET SystemRestoreConfiguration]] no longer fails when using non-native authentication.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.7.0 ===&lt;br /&gt;
Released March 2016. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* New maintenance job [[Built-in_Jobs#Disabled_Job_Cleanup_Job|Disabled Job Cleanup Job]].&lt;br /&gt;
* Support for parameters that are [[Implementing_Jobs#Parameterization|required at runtime]].&lt;br /&gt;
* Filter [[REST_Endpoints#GET_a_list_of_an_existing_job.27s_schedules|job schedules endpoint]] by start date and/or end date.&lt;br /&gt;
* Return [[REST_Endpoints#GET_details_of_an_existing_job|runtime parameters]] for schedule-based one-time runs.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* [[Embedded_API#Get_System_Restore_Configuration|SystemRestoreConfiguration]] export was only including the last schedule of a job.&lt;br /&gt;
* Admin-only hosts fail on startup if not in consistent timezone as cluster.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.6.1 === &lt;br /&gt;
Released February 2016. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* Enhance JobQueuer to reduce execution time when dealing with large numbers of chain-only jobs.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fix issue where job that is scheduled and chained is scheduled late after being chained.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.6.0 === &lt;br /&gt;
Released January 2016. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[Job_Forking|Forked Jobs]] offer jjdchinga tu mad[[Advanced_Configuration#Properties_File|Classpath com.carfey.properties]] no longer required. Applicable when using overrides.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* AutoInterrupt not being set on jobs from [[Initializing_and_Restoring#Jobs|System Restore]].&lt;br /&gt;
* RunningHostManager can, in rare cases, evaluate job metadata as a HostInformation entry.&lt;br /&gt;
* Schedule calculation on outage looks back one minute too far when considering pickup buffer.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.5.2 === &lt;br /&gt;
Released December 2015.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes === &lt;br /&gt;
* With native authentication, at least one Admin User must still exist when making [[Admin_User_Management#Changing_Users|Role Changes]].&lt;br /&gt;
* Cleanup of some internal host management functionality.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.5.1 === &lt;br /&gt;
Released November 2015.&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements === &lt;br /&gt;
* Obsidian nodes that are set to different timezones but have the same rules (functionally equivalent such as &#039;&#039;America/Los_Angeles&#039;&#039; and &#039;&#039;PST&#039;&#039;) may now be run together.&lt;br /&gt;
* [[Authenticator|LDAPAuthenticator]] now supports [[Advanced_Configuration#Authentication_Properties|non-dn user attribute logins]].&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* [[Admin_User_Management#Viewing_Rights_.26_Changing_Your_Password|My User]] screen would fail when using LDAPAuthenticator. Now shows authorized roles.&lt;br /&gt;
* Stale host and timezone records are predeleted on startup.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.5.0 === &lt;br /&gt;
Released November 2015. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements === &lt;br /&gt;
* Support for scanning and locating CGLIB-enhanced classes in [[Spring_Integration|Spring]] and Grails.&lt;br /&gt;
* Improved [[Admin_Job_Run_Time_Preview|runtime preview]] listing performance.&lt;br /&gt;
* Automatic logging of Obsidian version number no longer requires special configuration.&lt;br /&gt;
* [[Authenticator]] implementations including LDAP now support checking user validity when the &amp;quot;remember me&amp;quot; option is used to authenticate. (Remember Me feature removed in 7.0.0 - see [[Release_Notes#Obsidian_7.0.0|Release Notes]].)&lt;br /&gt;
* Runtime listing [[REST_Endpoints#GET_a_list_of_scheduled_runtimes_.28supports_multiple_jobs.29|REST endpoint]] and [[Embedded_API#List_Scheduled_Runtimes|Embedded API]] now support specifying a sort order, maximum quantity and parameter filtering, and now guarantee ordering by scheduled time.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes === &lt;br /&gt;
* Capped [[Admin_Job_Run_Time_Preview|runtime preview]] results now respect ascending ordering.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.4.1 === &lt;br /&gt;
Released September 2015. &lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Remove undesired field jobHistoryRunningId from runtime listing REST endpoint.&lt;br /&gt;
* Fixed standalone scripts that would not shutdown instance when hostname could not be determined.&lt;br /&gt;
* Shell scripts should always exit with non-zero code on error.&lt;br /&gt;
* Improve forked job shell script compatibility.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.4.0 === &lt;br /&gt;
Released September 2015. &lt;br /&gt;
&lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[Cron#Recurrence_Start_DateTime|Recurrence]] supports a start datetime.&lt;br /&gt;
* [[Implementing_Jobs#Interruptable_Jobs|Interruptable Jobs]] support [[Job_Features#Expected_Length_Handling_.28job_execution_duration.29|Auto Interruption]] when maximum expected run length is exceeded.&lt;br /&gt;
* [[Implementing_Jobs#Global_Parameters|Global Parameters]] support [[Implementing_Jobs#Global_Substitution_Mode|Global Substitution Mode]].&lt;br /&gt;
* Added &#039;&#039;jobClass&#039;&#039; parameter to job search in REST and Embedded APIs.&lt;br /&gt;
* Performance improvements in cron and recurrence calculations.&lt;br /&gt;
* Add database index for recovery performance.&lt;br /&gt;
* Support for [[Advanced_Configuration#Database_Properties|disabling internal connection pool]].&lt;br /&gt;
* Support [[Advanced_Configuration#Miscellaneous_Properties|base HREF overrides]] for load balancers, etc.&lt;br /&gt;
* JobHistoryCleanupJob always cleans up expired schedules according to &amp;lt;code&amp;gt;maxAgeDays&amp;lt;/code&amp;gt; parameter.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Default sequence_manager pool to 10% of connections with min of 2.&lt;br /&gt;
* Fix issue where parameter order is not maintained consistently on PostgreSQL&lt;br /&gt;
* Fix issue where JobHistoryCleanupJob misses cleaning up jobs that are in MISSED state.&lt;br /&gt;
* JobHistoryCleanupJob leaves latest runtime record to ensure Recurrence schedules fire at correct time.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.3.3 === &lt;br /&gt;
Released August 2015. &lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Chain Skipped and Conflict Missed job runtime enum values corrected to use underscores in REST/Embedded API when listing runtimes.&lt;br /&gt;
* In rare cases, outages or queuing delays could prevent first job history from ever being queued.&lt;br /&gt;
* Job initial schedule end time not editable via text entry when cloned.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.3.2 === &lt;br /&gt;
Released June 2015. &lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Restore missing fields on Runtime and RuntimeError classes for REST/Embedded API.&lt;br /&gt;
* Defensive handling of missing JOB_STATE records in chaining.&lt;br /&gt;
* Job class field in job edit screen loses content if enter is pressed without match.&lt;br /&gt;
* Obsidian version added to dispatcher logs.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.3.1 === &lt;br /&gt;
Released June 2015. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* Case-sensitive collation support for SQL Server.&lt;br /&gt;
* Unicode support on SQL Server (please note that existing schemas will not be upgraded automatically).&lt;br /&gt;
* Unicode fixes for all platforms.&lt;br /&gt;
* Whitespace characters permitted in between delimited schedules.&lt;br /&gt;
* Improved logging on warning and error conditions.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* JobSearch not working on Oracle since 3.3.0.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.3.0 === &lt;br /&gt;
Released May 2015. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[Implementing_Jobs#List_Parameterization|Job parameterization]] support for picklists of valid values.&lt;br /&gt;
* [[Implementing_Jobs#Dynamic_File_Lists|Server-side file listing]] support.&lt;br /&gt;
* Support for multiple [[Cron#Basic_Format|Cron]] schedule patterns.&lt;br /&gt;
* [[Admin_Job_Chains#Chain_Tree_View|Tree visualization of chains]].&lt;br /&gt;
* [[Admin_Job_Chains#Conditional_Chaining_Configuration|Conditional chains]] now support text ordinal comparisons.&lt;br /&gt;
* Added optional [[Admin_Job_Chains#Adding_or_Updating_a_Job_Chain|chain description]].&lt;br /&gt;
* Spawning and queuing performance improvements.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Expired job schedules are no longer deleted when new schedules are applied. A patch is introduced in this release to find and create placeholder schedules for these jobs.&lt;br /&gt;
* [[Built-in_Jobs#Job_History_Cleanup_Job|Job History Cleanup Job]] allowed configuration of maxAgeScheduleDays to be lower than maxAgeDays resulting in schedules being deleted for which there is still job history.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.2.0 === &lt;br /&gt;
Released April 2015. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[Cron#Recurrence|Recurrence]] job schedules.&lt;br /&gt;
* Upgrade to latest Java mail library.&lt;br /&gt;
* Support for determining user that initiated a [[Implementing_Jobs#Interruptable_Jobs|job interrupt]].&lt;br /&gt;
* Pickup time in [[Job Forking|forked jobs]] now reflects JVM startup to more accurately report execution times.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Adding new schedule with outstanding auto-retry execution no longer fails.&lt;br /&gt;
* Prevent license checks making extra calls.&lt;br /&gt;
* Ensure auto retries with 0 interval are executed as soon as possible, instead of after at least one minute.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.1.1 ===&lt;br /&gt;
Released April 2015. &lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fix critical bug where successful job runs after failures would be marked as failed due to job recovery handling.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.1.0 ===&lt;br /&gt;
Released March 2015. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* SQL Server 2008-2014 support.&lt;br /&gt;
* Improve event log messages.&lt;br /&gt;
* &amp;quot;?&amp;quot; now equivalent to &amp;quot;*&amp;quot; in cron schedules.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Prevent event log messages being dispatched before commit of source data.&lt;br /&gt;
* Fix delayed result set cleanup in job queuer in rare cases.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.0.1 ===&lt;br /&gt;
Released February 2015. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* Job schedule preview and update performance improvements.&lt;br /&gt;
* Job delete performance improvements for jobs with large number of history records.&lt;br /&gt;
* [[Implementing_Jobs#Classpath_Scanning|Classpath scanning]] interval now configurable (useful for job forking).&lt;br /&gt;
* [[Built-in_Jobs#Job_History_Cleanup_Job|JobHistoryCleanupJob]] now supports deleting old schedules.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Jobs running on hosts that are disabled during execution will now flag them as FAILED or COMPLETED as expected.&lt;br /&gt;
* Ensure identical job schedule states which butt against each other are merged.&lt;br /&gt;
* Set catalina.base property at Winstone startup so default log configuration works.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.0.0 ===&lt;br /&gt;
Released February 2015. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[Job Forking|Job forking]] added to support distinct JVM instances for each job execution.&lt;br /&gt;
* Full API expansion. APIs now support all functionality exposed by the admin web application. See [[REST Endpoints|REST API]] and [[Embedded API]] for details.&lt;br /&gt;
* Pretty URLs and login redirects in admin web application, including bookmark support.&lt;br /&gt;
* [[Clustering#Host_Preference|Host preference]] support.&lt;br /&gt;
* Full [[Initializing and Restoring|initialization and restoration]] support for Obsidian instances.&lt;br /&gt;
* Auto-polling for job history, logs and sent notifications views in admin web application.&lt;br /&gt;
* [[Admin_Custom_Calendars#Deleting|Custom calendar delete]] support. Also available in APIs.&lt;br /&gt;
* Convenience database [[Upgrading_Obsidian#Database_Upgrade_Scripts|upgrade]] and [[Getting_Started#Database_Creation_Scripts|create]] scripts included in installation bundle. Automatic version upgrades still supported.&lt;br /&gt;
* Better [[Implementing_Jobs#Inheritance|inheritance options]] for defined job parameters.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fix LAST recovery type when job left in READY state.&lt;br /&gt;
* Redirect to proper 404 and error pages.&lt;br /&gt;
* Optimize persistence calls to better handle large number of schedules per job.&lt;br /&gt;
* Fix LDAPAuthenticator group membership attribute lookup.&lt;br /&gt;
* Explicitly shut down daemon connection reaper thread at Obsidian shutdown.&lt;br /&gt;
* Fix minor bugs with schedule merges and splits.&lt;br /&gt;
* Fix warning shown in rare cases on job history listing.&lt;br /&gt;
* Minor help user interface fixes.&lt;br /&gt;
* Minor job chain user interface fixes.&lt;br /&gt;
* Prevent system parameter sort order changing on updates.&lt;br /&gt;
* Fix record duplicates in job history and corresponding APIs caused by multiple chains which have the same source and target jobs.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.9.1 === &lt;br /&gt;
Released November 2014. &lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fixed issue where auto retry caused failure during a Disabled window.&lt;br /&gt;
* Fixed filenames on screen data downloads.&lt;br /&gt;
* Fixed issue with Last recovery when last executed time to current time spanned a Disabled window.&lt;br /&gt;
* Graceful handling of host designator update interruptions.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.9.0 === &lt;br /&gt;
Released November 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[Conflicts#Jobs_in_Multiple_Conflict_Sets|Jobs in multiple conflict sets]].&lt;br /&gt;
* Add ad hoc flag to Context object supplied to SchedulableJob instances.&lt;br /&gt;
* Improved [[Admin_Job_Chains#Conditional_Chaining_Configuration|chain detail]] messages in [[Admin_Job_History_(3.x.x and earlier)#Job_History_Details|job history details]] table.&lt;br /&gt;
* Ability to set the host designator for multiple Obsidian instances in the same JVM via &amp;lt;code&amp;gt;com.carfey.obsidian.schedulerDesignation&amp;lt;/code&amp;gt; in the [[Advanced Configuration|properties file]].&lt;br /&gt;
* Job queuing performance improvements&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Ensure license check multiple is at least 2 in [[Admin_System_(3.x.x and earlier)|System]] tab.&lt;br /&gt;
* When recovering jobs which had died, new jobs were flagged as Overlapped instead of Missed.&lt;br /&gt;
* All recovery type resulting in Abandoned jobs incorrectly.&lt;br /&gt;
* Breadcrumbs incorrect when cloning a job.&lt;br /&gt;
* JNDI cannot be used when the key does not start with &amp;quot;java:&amp;quot; prefix. &amp;lt;ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using automated installer files from previous releases and are using JNDI, you may add &amp;lt;pre&amp;gt;&amp;lt;entry key=&amp;quot;database.jndi.name&amp;quot; value=&amp;quot;yourJNDIPath&amp;quot;/&amp;gt;&amp;lt;/pre&amp;gt; anywhere under the userInput element of UserInputPanel.3 and empty out the value for &#039;&#039;&#039;database.url&#039;&#039;&#039;. For example,&lt;br /&gt;
  &amp;lt;com.izforge.izpack.panels.UserInputPanel id=&amp;quot;UserInputPanel.3&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;userInput&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;db.schema&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&#039;&#039;&amp;lt;entry key=&amp;quot;database.url&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;database.password&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;database.table.prefix&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;database.connection.timeout&amp;quot; value=&amp;quot;2000&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;database.username&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&#039;&#039;&amp;lt;entry key=&amp;quot;database.jndi.name&amp;quot; value=&amp;quot;java:comp/env/jdbc/obsidianJNDI&amp;quot;/&amp;gt;&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;database.max.connections&amp;quot; value=&amp;quot;40&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;include.all.jdbc&amp;quot; value=&amp;quot;false&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;/userInput&amp;gt;&lt;br /&gt;
  &amp;lt;/com.izforge.izpack.panels.UserInputPanel&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/ref&amp;gt;&lt;br /&gt;
* Database connections not rolled back just before close when removed from the pool, resulting in warning log messages.&lt;br /&gt;
* Sanitize HTTP headers, email subjects, etc. to prevent injection issues.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.8.1 === &lt;br /&gt;
Released September 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* New connection pool reaper to draw down pool size if usage allows.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* More explicit handling of connection pool emptying on Obsidian shutdown.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.8.0 === &lt;br /&gt;
Released September 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* Login popup when session expires in [[Admin_Web_Application_Guide|admin web application]]. Users can now log back in without losing their changes.&lt;br /&gt;
* Support for using [[Advanced_Configuration|context classloader]] via configuration property &#039;&#039;com.carfey.jdk.useContextClassLoader&#039;&#039;.&lt;br /&gt;
* Reduced number of connections required for normal operation. Previously, up to 1 connection per job per minute was required.&lt;br /&gt;
* Minor user interface improvements.&lt;br /&gt;
* Support for re-queuing jobs rejected from the job thread pool (via parameter &#039;&#039;queueJobsOnPoolRejection&#039;&#039;).&lt;br /&gt;
* Improved default log4j rolling file appender configuration.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fix NullPointerException on job recovery when last job was rejected by the thread pool.&lt;br /&gt;
* Fix rare instances of licenses not being released due to token file issues&lt;br /&gt;
* Fix poor validation message on saving if invalid boolean system parameter.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.7.1 === &lt;br /&gt;
Released August 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[REST_API#Authentication_.26_Authorization|REST authentication]] can be configured to be optional.&lt;br /&gt;
* Cron pattern expansion. Support range increments (e.g. 3-20/5)&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fix NullPointerException in handling mdoodood..dkididkdkd&lt;br /&gt;
IP changes.&lt;br /&gt;
* Upgraded to TLD 2.0 fixing Obsidian WAR deployment in JBoss 6.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.7.0 === &lt;br /&gt;
Released July 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[Admin_Notifications#Job_Execution_Subscriptions|Re-tooled job execution notifications]], including [[Admin_Notifications#Conditional_Job_Notification_Configuration|conditional notifications]]&lt;br /&gt;
* We have open-sourced all of our [[Built-in_Jobs | built-in jobs]]!&lt;br /&gt;
* File-based job [[Implementing_Jobs#Initializing_Job_Schedules|initialization]].&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fix page jumping when modifying job chain conditions on long pages.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.6.1 === &lt;br /&gt;
Released June 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* Cygwin support in startup/shutdown scripts.&lt;br /&gt;
* Run parameters override configured parameters.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Oracle CLOB field storage fix.&lt;br /&gt;
* Fix single-node mode on host with no recognizable NIC.&lt;br /&gt;
* Prevent currently or soon to be running jobs from being deleted.&lt;br /&gt;
* Fix multiple job history parameters failing on H2.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.6.0 ===&lt;br /&gt;
Released May 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[Implementing_Jobs#Classpath_for_Building_and_Deploying|Job implementation metadata stored in database]]. Admin UI deployments no longer require Job Implementations on the classpath in order to schedule and configure.&lt;br /&gt;
* Delete Chain support in [[Admin_Job_Chains#Deleting|UI]], [[Embedded_API#Delete_a_Job_Chain|Embedded API]] and [[REST_Endpoints#DELETE_an_existing_job_chain|REST API]]&lt;br /&gt;
* Hardware-linked licensing supported in public Obsidian bundle.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fixed [[Implementing_Jobs#Classpath_Scanning|Classpath Scanning]] to work in WebLogic 12c.&lt;br /&gt;
* Fixed chain recovery date ranges being overly broad.&lt;br /&gt;
* Ensure any stale timezone records are cleaned up.&lt;br /&gt;
* Removed some libraries stored multiple times in installer.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.5.1 ===&lt;br /&gt;
Released April 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* Expose convenience JobId and Nickname getters in [[Implementing_Jobs#SchedulableJob_Interface|Context]].&lt;br /&gt;
* Improved performance of chaining recovery.&lt;br /&gt;
* [[Installation_Guide#Configuring_3rd_Party_Library_Conflict_Management|JarJar]] bytecode modification support now includes gson.&amp;lt;ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using automated installer files from previous releases, you may add &amp;lt;pre&amp;gt;&amp;lt;entry key=&amp;quot;jarjar.gson&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;&amp;lt;/pre&amp;gt; or &amp;lt;pre&amp;gt;&amp;lt;entry key=&amp;quot;jarjar.gson&amp;quot; value=&amp;quot;false&amp;quot;/&amp;gt;&amp;lt;/pre&amp;gt; according to your desired usage anywhere under the userInput element of UserInputPanel.14. For example,&lt;br /&gt;
 &amp;lt;com.izforge.izpack.panels.UserInputPanel id=&amp;quot;UserInputPanel.14&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;userInput&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&#039;&#039;&amp;lt;entry key=&amp;quot;jarjar.gson&amp;quot; value=&amp;quot;false&amp;quot;/&amp;gt;&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;jarjar.jxl&amp;quot; value=&amp;quot;false&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;jarjar.dom4j&amp;quot; value=&amp;quot;false&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;jarjar.jmustache&amp;quot; value=&amp;quot;false&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;jarjar.opencsv&amp;quot; value=&amp;quot;false&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;jarjar.h2&amp;quot; value=&amp;quot;false&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;/userInput&amp;gt;&lt;br /&gt;
 &amp;lt;/com.izforge.izpack.panels.UserInputPanel&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/ref&amp;gt;&lt;br /&gt;
* Introduced support for having a scheduler and non-scheduler instance running in same class-space.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Improved failure messaging when a scheduler node is started in a different time zone than the current nodes in the cluster.&lt;br /&gt;
* Chain search filters aren&#039;t saved.&lt;br /&gt;
* Exclude unneeded web jars from standalone installations.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.5.0 === &lt;br /&gt;
Released March 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[Admin_Jobs#Execution_.26_Pickup|Retry Interval]] support for Auto Retry of failed jobs.&lt;br /&gt;
* [[Admin_Global_Parameters|Global Job Parameter]] support.&lt;br /&gt;
* [[Installation_Guide#JNDI_Configuration|JNDI Support]] for Database Connections and Email Sessions.&lt;br /&gt;
* MariaDB 5.5 support.&lt;br /&gt;
* JBoss AS 7 support.&lt;br /&gt;
* Text filter on Job Chain View.&lt;br /&gt;
* [[Installation_Guide#Configuring_3rd_Party_Library_Conflict_Management|JarJar]] bytecode modification support during installation to avoid dependency conflicts.&lt;br /&gt;
* Embedded API Javadoc artifact included in installation.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Support */2 cron expressions, [[Admin_Jobs#Edit.2C_View_.26_Change_Schedules|Schedule Inputs]] extended to 255 characters.&lt;br /&gt;
* REST API - [[REST_Endpoints#GET_a_list_of_jobs|JobListing]].&amp;lt;code&amp;gt;getJobs()&amp;lt;/code&amp;gt; was returning &amp;lt;code&amp;gt;List&amp;amp;lt;Job&amp;amp;gt;&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;List&amp;amp;lt;JobState&amp;amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Support multiple keys in key server proxy. &lt;br /&gt;
* Support VFS URLs in [[Implementing_Jobs#Classpath_Scanning|Classpath Scanner]] for JBoss.&lt;br /&gt;
* Postgres failed on blank schema.&lt;br /&gt;
* Job resubmission validates that job isn&#039;t disabled for next minute instead of current minute.&lt;br /&gt;
* Embedded API - [[Embedded_API#Delete_a_Job|Delete Job]] would fail if job class has since been removed from classpath.&lt;br /&gt;
* [[Admin_Jobs#Job_Listing|Job Listing]] schedule/state defaults to next minute.&lt;br /&gt;
* Filters aren&#039;t saved consistently on search screens and never saved for runtime preview.&lt;br /&gt;
* Table settings aren&#039;t saved on [[Admin_Jobs#Job_Listing|Job Listing]].&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.4.1 === &lt;br /&gt;
Released February 2014. &lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fix installer not working in console mode.&lt;br /&gt;
* Ensure that defined job parameters and their values are returned in the original configured order.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.4.0 === &lt;br /&gt;
Released February 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* New [[Getting_Started|graphical installer and simplified startup scripts]].&lt;br /&gt;
* Full support for job chaining and conflicts in the [[REST API]] and [[Embedded API]].&lt;br /&gt;
* Revamped WebSphere support (8.5).&lt;br /&gt;
* [[Scripting Jobs]] can now be configured with a script file path instead of the script itself.&lt;br /&gt;
* New [[Built-in_Jobs#MySqlBackupJob|MySqlBackupJob]] built-in job.&lt;br /&gt;
* Informational message logged when a new version of Obsidian is available.&lt;br /&gt;
* [[Admin_User_Management#Deleting_Users|Delete user]] support.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fixed job execution recovery not working in some cases when the database is temporarily unavailable.&lt;br /&gt;
* Fix PostgreSQL connections not released from pool properly when connection is invalid.&lt;br /&gt;
* Fix [[Implementing_Jobs#Classpath_Scanning|Classpath Scanning]] on WebSphere.&lt;br /&gt;
* Fix UI rendering issue on Jobs screen on WebSphere.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.3.1 === &lt;br /&gt;
Released December 2013. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* New [[Built-in_Jobs#Script_Job|Script File Job]]&lt;br /&gt;
* Improved responsiveness of job interrupts for built-in jobs.&lt;br /&gt;
* UI tweak to [[Jobs]] screen to avoid displaying custom calendars when not in use.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Update default log configuration for Tomcat to use &amp;lt;code&amp;gt;catalina.base&amp;lt;/code&amp;gt;, which works consistently on *nix platforms.&lt;br /&gt;
* Remove redundant event logging for job success and failure in the Job Spawner category.&lt;br /&gt;
* Handle releasing licenses even when temp directory is not writable, and attempt to use working directory as a fallback.&lt;br /&gt;
* Job names containing ampersands did not work as filters in search screens.&lt;br /&gt;
* Host designator assigned earlier in startup to ensure logging accurately reports the host.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.3.0 === &lt;br /&gt;
Released November 2013. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* Fully unified [[Embedded API]] which contains same operations and has same semantics as the [[REST API]]. The previous Legacy API is now deprecated.&lt;br /&gt;
* [[REST API]] bean classes bundled in &amp;lt;code&amp;gt;obsidian.jar&amp;lt;/code&amp;gt; for simplified Java integration.&lt;br /&gt;
* Submission of [[Admin_Jobs#Ad_Hoc_.26_One-Time_Run_Submission|one-time job runs]] via the UI now allows specifying exact minutes instead of 5 minute intervals.&lt;br /&gt;
* [[Chaining]] evaluation is now fully recoverable. Processing of chaining now processed independently of job execution.&lt;br /&gt;
* Dozens of UI tweaks and improvements.&lt;br /&gt;
* Stale hosts in [[Systems|Systems tab]] now are pruned after about an hour of inactivity. Host disabling is preserved if the node is restored.&lt;br /&gt;
* Graceful shutdown in Winstone for [[Getting Started|quick start]] method.&lt;br /&gt;
* [[Jobs#Custom_Calendars|Custom Calendars]] can now be specified with multiple delimiters, including new lines and spaces.&lt;br /&gt;
* [[Email Templates]] can now include the event category.&lt;br /&gt;
* Track most recent upgraded version in the database.&lt;br /&gt;
* Job runtime [[REST_Endpoints#GET_a_list_of_scheduled_runtimes_.28supports_multiple_jobs.29|REST endpoint]] now defaults to a more useful previous 24-hour window.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Chain All setting not shown correctly when editing existing [[Jobs]].&lt;br /&gt;
* Abandoned, Missed and Conflict Missed chaining never fires.&lt;br /&gt;
* Current scheduled state not shown correctly for existing jobs in [[Jobs]] in Winstone.&lt;br /&gt;
* Export on [[Logs]] contained extra text in ID column.&lt;br /&gt;
* Chained jobs now respect host restrictions.&lt;br /&gt;
* Correct custom calendar reversed PUT/POST for [[REST API]], and implement standardized date formatting.&lt;br /&gt;
* UI table listings no longer flash error message when quickly navigating away during a request.&lt;br /&gt;
* Legacy API calls no longer incorrectly start non-daemon threads for notifications and event logging.&lt;br /&gt;
* Lock issues when evaluating many [[Chaining|chained jobs]].&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.2.1 ===&lt;br /&gt;
Released October 2013. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* Enhanced [[Spring_Integration|Spring Integration]] to remove need for distinct [[Implementing_Jobs#Classpath_Scanning|Classpath Scanning]]. Jobs can be auto detected from Spring context. &lt;br /&gt;
* Enhanced [[Job_Features#Host_Restrictions_or_Affinity|Running Hosts Restrictions]]. Now a configuration value can be used to determine if these restrictions apply to Ad Hoc jobs. &lt;br /&gt;
* Ability to [[Getting_Started#Disabling_Automatic_Database_Updates|disable database updates]] on startup. &lt;br /&gt;
* Add helpful text to job screen indicating you can type in a job class Obsidian &lt;br /&gt;
* Additional Spring support for [[Spring_Integration#Wiring_an_Embedded_Obsidian_Scheduler|embedded schedulers]].&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Corrected issue where classpath scanning configuration values were being overridden back to default on each restart (introducted in 2.1). &lt;br /&gt;
* Fix SchedulerStarter shutting down JVM in embedded mode.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.2.0 ===&lt;br /&gt;
Released October 2013.&lt;br /&gt;
==== Features / Enhancements ====&lt;br /&gt;
* [[Spring_Integration|Spring]] Dependency Injection support.&lt;br /&gt;
* [[Built-in_Jobs#File_Scanner_Job|File scanner]] job.&lt;br /&gt;
* [[Advanced_Configuration#Dependent_Libraries|Python and Groovy]] libs updated. Python from jython.jar (version 2.5.2rc2) to jython-standalone-2.5.3.jar. Groovy from groovy-all-1.7.6.jar to groovy-all-2.1.8.jar.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* DBAuthenticator is now defaulted configuration. Improved handling of bad configuration on startup.&lt;br /&gt;
* Obsidian web app startup no longer attempts to start the scheduler with certain types of unrecoverable bad configuration.&lt;br /&gt;
* Fixed issue where classpath scanner only worked in Admin Web Application if running with an embedded scheduler.&lt;br /&gt;
* Fixed issue where Auto Failure Retry combined with triggered Job Chaining resulted in chaining failure.&lt;br /&gt;
* Cleaned up Job History Search Results File Export that could contain garbage characters.&lt;br /&gt;
* Fixed issue where job runtime preview fails when only disabled job state exists.&lt;br /&gt;
* Fixed TRACE logging level - No logging output if level explicitly set to TRACE.&lt;br /&gt;
* Remove unnecessary JAR files bundled with the installation package.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.1.1 ===&lt;br /&gt;
Released September 2013.&lt;br /&gt;
==== Features / Enhancements ====&lt;br /&gt;
* [[Admin_Jobs#Run_Parameters|Ad Hoc / One-Time Run Job Parameterization]].&lt;br /&gt;
* Obsidian license purchases via Stripe.&lt;br /&gt;
* All Carfey Software libraries bundled into single obsidian.jar.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* Postgres schema support - only supported public schema.&lt;br /&gt;
* LogCleanupJob does not validate specified LEVEL attribute.&lt;br /&gt;
* LDAPAuthenticator assumed &#039;&#039;cn&#039;&#039; in building &#039;&#039;dn&#039;&#039; for authentication. Now configurable - see [[Advanced_Configuration#Configuration_Details|Advanced Configuration]].&lt;br /&gt;
* LDAPAuthenticator assumed only &#039;&#039;uniqueMember&#039;&#039; for group membership. Now also checks other common attributes, supporting ActiveDirectory. See [[Authenticator#Customizing_our_LDAP_Authenticator|LDAP Authenticator]].&lt;br /&gt;
* &#039;&#039;run.obsidian&#039;&#039; target execution classpath included in Obsidian bundle includes non-jar/non-resource files. &lt;br /&gt;
* Site license verification runs once on startup instead of every lease cycle.&lt;br /&gt;
* Date parsing issue related to Locales on retrieving and refreshing license. Fixed in [http://sourceforge.net/projects/carfeydate/ release 1.2 of carfey-date].&lt;br /&gt;
* In rare cases, job results in Job History listing could be mangled.&lt;br /&gt;
* Proxy key server would fail on requests when run from time zones different from the master key server.&lt;br /&gt;
* Oracle job results limited to 2000 characters - now unlimited on all platforms.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.1 ===&lt;br /&gt;
Released July 2013.&lt;br /&gt;
==== Features / Enhancements ====&lt;br /&gt;
* [[Obsidian_Tables|Table name prefix]] support.&lt;br /&gt;
* [[Licenses &amp;amp; Nodes|Site license]] support in public Obsidian bundle.&lt;br /&gt;
* [[Advanced_Configuration#Properties_File|Programmatic properties]] override support.&lt;br /&gt;
* Alternate [[Advanced_Configuration#Configuration_Details|schema user/owner]] support. Additional Oracle details [[Obsidian_Tables#Oracle_Privileges|here]].&lt;br /&gt;
* [[Admin_Notifications#Deleting_a_Subscriber|Delete subscriber]] support.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* Ensure all [[Admin_Logs|Event Log]] messaging to always use Display Name for enums.&lt;br /&gt;
* Expand script job disabling to disable all potentially dangerous jobs.&lt;br /&gt;
* Ensure internal class AnnotatedSchedulableJob is always excluded from available class listing in [[Admin_Jobs#Job_Nickname_and_Class|Job Admin]].&lt;br /&gt;
* Upgraded to more recent Oracle JDBC driver (11.2.0.3)&lt;br /&gt;
* Various database metadata optimizations&lt;br /&gt;
* [[Implementing_Jobs#Classpath_Scanning|Classpath scanner]] rescans on parameter changes.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.0 ===&lt;br /&gt;
Released June 2013.&lt;br /&gt;
==== Features / Enhancements ====&lt;br /&gt;
* [[Implementing_Jobs#Classpath_Scanning|Classpath Scanning]] to automatically populate Job UI with available jobs.&lt;br /&gt;
* Fully customizable [[Email_Templates|Email Templates]].&lt;br /&gt;
* New [[Embedded_API|Embedded API]] operations exposed so job configuration can be manipulated without REST API or UI.&lt;br /&gt;
* [[Admin_Jobs#Custom_Calendars|Custom Calendar]] support to add date-based exclusions to job schedules (e.g. holidays).&lt;br /&gt;
* New [[Admin_User_Management#User_Rights|Limited Read]] role which restricts access to potentially sensitive configuration.&lt;br /&gt;
* New [[Built-in_Jobs|Built-in Jobs]] - file archival and file clean up.&lt;br /&gt;
* Easy [[Admin_Jobs#Job_Listing|job cloning]] in UI.&lt;br /&gt;
* [[Built-in_Jobs#Shell_Script_Jobs|Shell scripting]] job support.&lt;br /&gt;
* Additional [[Admin_Jobs#Job_Edit_Screen|job execution options]] - auto-retries on failure and new &amp;quot;Chain All&amp;quot; chaining mode.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* Improve logging of some trapped exceptions.&lt;br /&gt;
* Improve handling of spawn locks to reduce contention.&lt;br /&gt;
* Fix standalone execution from Ant script.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 1.5.2 ===&lt;br /&gt;
Released May 2013.&lt;br /&gt;
==== Features / Enhancements ====&lt;br /&gt;
* Job nickname lengthened from 50 to 255 characters to allow more specific nicknames.&lt;br /&gt;
* Database metadata loading restricted to known Obsidian application tables.&lt;br /&gt;
* Add support in [[REST_Endpoints#Job_Endpoints|job listing endpoint]] for nickname and job custom parameter tag search.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* Fixed JobHistoryCleanupJob failures that occurred when a record to be cleaned up had been interrupted.&lt;br /&gt;
* Disallow failed job resubmission if the job is currently running. This prevents these resubmissions from potentially being ABANDONED due to execution duration exceeding pickup buffer minutes specified on the job. Job is available for resubmission once currently running job completes.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 1.5.1 ===&lt;br /&gt;
Released February 2013.&lt;br /&gt;
==== Features / Enhancements ====&lt;br /&gt;
* [[Implementing_Jobs#Interruptable_Jobs|Interruptable Jobs]], including new [[REST_API|REST API]] endpoint.&lt;br /&gt;
* Spawning improvements to seek minute boundaries more consistently.&lt;br /&gt;
* Runtime preview now defaults to start time to current time.&lt;br /&gt;
* Improved license handling for paid licenses to increase reliability in case of outages.&lt;br /&gt;
* A handful of minor UI tweaks and additions.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* Fix connection handling when database connectivity is lost in spawner or queuer threads.&lt;br /&gt;
* Fix criteria causing some running conflicts to be not found resulting in conflicted jobs running concurrently.&lt;br /&gt;
* Improve messaging and details when failing to connect to MySQL.&lt;br /&gt;
* Various UI fixes.&lt;br /&gt;
* Events for jobs would sometimes be dispatched for the wrong target.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 1.5 ===&lt;br /&gt;
Released January 2013.&lt;br /&gt;
==== Features / Enhancements ====&lt;br /&gt;
* [[REST_API|REST API]] for a complete range of job, scheduling and host management features.&lt;br /&gt;
* Export support in Excel, CSV and XML in [[Admin_Job_History_(3.x.x and earlier)#Exporting_Results|Job History]], [[Admin_Jobs#Exporting_Results|Job]], [[Admin_Job_Run_Time_Preview|Job Runtimes]], [[Admin_Logs#Exporting_Results|Log]] and [[Admin_Notifications#Sent_Notifications_Screen|Sent Notifications]] views.&lt;br /&gt;
* Expected job run time parameters - [[Admin_Jobs#Execution_.26_Pickup|See Expected  Length]].  Events trigger in cases where execution duration is outside of defined threshold.&lt;br /&gt;
* Display actual run duration in job history view&lt;br /&gt;
* Enhance run once future date pattern to match actual future date.  Contributes to consistency in job state absorption in new overlapping state ranges.&lt;br /&gt;
* Allow single node to run without valid lease&lt;br /&gt;
* Add 90/120 minutes pickup buffer option&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* Ad hoc submission validation text inconsistent&lt;br /&gt;
* Job set to ad hoc active state would not allow one-time immediate submissions.&lt;br /&gt;
* Workaround String indexoutofbounds issue with winstone server&lt;br /&gt;
* Read-only users can submit one-time job runs.&lt;br /&gt;
* Can&#039;t save unscheduled active job in postgresql&lt;br /&gt;
* After saving new user, help icon disappears&lt;br /&gt;
* MySQL - Audit columns hit truncation error when registered user is too long&lt;br /&gt;
* Various display bugs&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 1.4.2 ===&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* Lock wait timeout fix&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 1.4.1 ===&lt;br /&gt;
==== Features / Enhancements ====&lt;br /&gt;
* Conditional [[Chaining]] support.&lt;br /&gt;
* [[Admin_Jobs#Deleting|Delete]] job support.&lt;br /&gt;
* [[Admin_Jobs#Job_Listing|Job]] view filter inclusivity those with only future states.&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* Default initial sorts case insenstive&lt;br /&gt;
* Status scheduling allowed for gaps&lt;br /&gt;
* Failed conditional chain prevents scheduling of job&lt;br /&gt;
* Schedule preview isn&#039;t ordered by effective date&lt;br /&gt;
* JobHistory order by clause sometimes ignored&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 1.4 ===&lt;br /&gt;
==== Features / Enhancements ====&lt;br /&gt;
* View of saved job history [[Admin_Job_History_(3.x.x and earlier)#Job_History_Details|results]]. &lt;br /&gt;
* Add schedule shortcut link on Job view&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* Various display bugs&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Footnotes ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Getting_Started&amp;diff=4163</id>
		<title>Getting Started</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Getting_Started&amp;diff=4163"/>
		<updated>2026-07-31T15:11:41Z</updated>

		<summary type="html">&lt;p&gt;Craig: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This Getting Started is for Obsidian 4.0 and newer versions.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This guide will help you choose a deployment setup that works for you, and then get it running. Before reading this page, you may wish to review Obsidian&#039;s [[Deployment Models]] to understand the different parts of Obsidian or to find out what deployment model works for you.&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve are looking to start writing your own Obsidian jobs, see [[Implementing Jobs]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Supported Platforms =&lt;br /&gt;
&lt;br /&gt;
Please quickly review our supported platforms before continuing.&lt;br /&gt;
&lt;br /&gt;
* Obsidian is OS-independent, and runs on the Java Virtual Machine. It works in a variety of environments, including Linux, OSX and Windows, and can be run inside virtual machines.&lt;br /&gt;
* Obsidian 5.x.x, 6.x.x and 7.x.x run on Java 11 or above (Obsidian 4.x.x runs on Java 1.7 or above). A JRE is sufficient — the embedded Tomcat runtime does not compile JSPs and needs no JDK.&lt;br /&gt;
* Obsidian&#039;s administration web application is a servlet application.&lt;br /&gt;
** 7.x.x has been tested on Tomcat 10.x&lt;br /&gt;
** 6.x.x has been tested on Tomcat 10.x and Jetty 11.x (Jakarta Servlet 5.0)&lt;br /&gt;
** 5.x.x has been tested on Tomcat 9.x and Jetty 10.x (Javax Servlet 4.0)&lt;br /&gt;
** 4.x.x has been tested on Tomcat 7, 8 &amp;amp; 8.5 and Jetty 9.x (Javax Servlet 4.0)&lt;br /&gt;
* It works on all modern browsers and is tested on recent versions of Chrome, Edge and Firefox.&lt;br /&gt;
* Obsidian requires one of the following fully-supported database platforms:&lt;br /&gt;
** MySQL 8.0.x (Obsidian 5.x and higher), 5.5-5.7 (excluding Galera)&lt;br /&gt;
** MariaDB 5.5&lt;br /&gt;
** Oracle 18c, 19c, 21c on Obsidian 5.x and higher&lt;br /&gt;
** Oracle 10g, 11.x, 12c on versions prior to Obsidian 5.0.0&lt;br /&gt;
** PostgreSQL 11-16 on Obsidian 5.x and higher&lt;br /&gt;
** PostgreSQL 9, 10&lt;br /&gt;
** MS SQL Server 2008-2019&lt;br /&gt;
** H2 1.4. &lt;br /&gt;
** &#039;&#039;Larger major versions are likely to work without issues but are not officially supported.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; With some exceptions, clustered scheduler instances require access to contact license servers over the Internet, or access to an internal proxy license server. See [[Licenses &amp;amp; Nodes]] for more information.&lt;br /&gt;
&lt;br /&gt;
== Hardware Requirements ==&lt;br /&gt;
&lt;br /&gt;
We recommend the following minimum hardware for typical installations:&lt;br /&gt;
&lt;br /&gt;
* 1 GHz processor&lt;br /&gt;
* 2 GB RAM&lt;br /&gt;
* 1 GB hard disk space (excluding accumulated logs)&lt;br /&gt;
&lt;br /&gt;
Note that actual requirements may vary depending on job volume, clustering and other factors.&lt;br /&gt;
&lt;br /&gt;
= Choose Your Installation Type =&lt;br /&gt;
&lt;br /&gt;
Obsidian consists of two main processes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Scheduler&#039;&#039;&#039; - schedules and executes jobs, sends notifications, etc. Also exposes the [[Embedded API]].&lt;br /&gt;
* &#039;&#039;&#039;Admin Web Application&#039;&#039;&#039; - provides management and monitoring UI, plus the [[REST API]] and [[Embedded API]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
These can be run together or separately in the following configurations. You will pick one or more of these options together to provide both the scheduler and admin web application functionality.&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Standalone Scheduler&#039;&#039;&#039; - A Java process that performs job execution. A standalone scheduler installation is generated when you run the Obsidian installer, and may be customized by adding JAR files containing your job code. It is started and stopped by scripts included with the installation. Requires a separate admin web application to be deployed.&lt;br /&gt;
# &#039;&#039;&#039;Embedded Scheduler&#039;&#039;&#039; - Provides job execution like a standalone scheduler, but instead runs as a process embedded within and started from your application. This eliminates the need to customize and deploy a separate artifact which will perform job execution. Requires a separate admin web application to be deployed.&lt;br /&gt;
# &#039;&#039;&#039;Standalone Admin Web Application&#039;&#039;&#039; - A Java servlet application providing Obsidian&#039;s administration capabilities but no scheduling services.  A standalone admin application WAR file is generated when you run the Obsidian installer, and may be customized by adding JAR files containing your job code.  Requires a separate standalone, embedded or combined scheduler and web application to be deployed for job execution to be performed.&lt;br /&gt;
# &#039;&#039;&#039;Combined Scheduler and Admin Web Application&#039;&#039;&#039; - A version of the admin web application which also runs a scheduler service for job execution. A combined scheduler and admin application WAR file is generated when you run the Obsidian installer, and may be customized by adding JAR files containing your job code. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Regardless of the deployment configuration, any time you run more than one scheduler process operating against the same Obsidian database, they will automatically form a cluster and share in job execution.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Choosing which installation type works best for you depends on your specific needs. The setups which are suitable for most cases are, in order: &lt;br /&gt;
&lt;br /&gt;
* Using an &#039;&#039;&#039;Embedded Scheduler&#039;&#039;&#039; (option 2), along with a &#039;&#039;&#039;Standalone Admin Web Application&#039;&#039;&#039; (option 3). This approach lets you embed Obsidian into your existing application without having to alter the Obsidian artifacts to include your job code. Instead, Obsidian&#039;s required libraries are included in your application and they are deployed together.&lt;br /&gt;
* Using a  &#039;&#039;&#039;Combined Scheduler and Admin Web Application&#039;&#039;&#039; (option 4) alone or in a cluster. This is suitable if you don&#039;t have another application containing job code for Obsidian to execute, or if you simply want to use Obsidian&#039;s scripting support, which doesn&#039;t require deploying JAR files.&lt;br /&gt;
&lt;br /&gt;
= Downloading Obsidian =&lt;br /&gt;
Just go to our [https://web.obsidianscheduler.com/download/ download page], and grab the latest Obsidian&#039;s installation zip file. There is no need to register or obtain a license before downloading.&lt;br /&gt;
&lt;br /&gt;
Once you&#039;ve downloaded the zip file, extract it to a directory of your choice.&lt;br /&gt;
&lt;br /&gt;
If you are trying to upgrade to a newer version of Obsidian, see [[Upgrading_Obsidian|upgrade]] instructions.&lt;br /&gt;
&lt;br /&gt;
= Quick Start for Evaluation Purposes =&lt;br /&gt;
&lt;br /&gt;
If you wish to quickly try Obsidian without minimal setup required, follow these steps. Otherwise, skip ahead to [[#Initial Setup|Initial Setup]].&lt;br /&gt;
&lt;br /&gt;
== Really Quick Method Using Embedded Database ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;We do not recommend this setup for production use. &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Ensure you have a JDK (11 or up for Obsidian 6.x.x &amp;amp; 5.x.x, 1.7 or up for Obsidian 4.x.x) installed and that the &amp;lt;code&amp;gt;JAVA_HOME&amp;lt;/code&amp;gt; environment variable is set to your JDK installation directory.&lt;br /&gt;
# Unzip the Obsidian download zip file to a directory of your choice.&lt;br /&gt;
# At the command line, in the zip extraction directory, run the [[Installation_Guide|installer]] using this command, substituting the appropriate Obsidian version number: &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar h2-tomcat-quick-start.xml&amp;lt;/code&amp;gt;&lt;br /&gt;
# Start Obsidian using the command: &amp;lt;code&amp;gt;./webObsidian.sh start scheduler&amp;lt;/code&amp;gt; for Linux or &amp;lt;code&amp;gt;webObsidian.bat start scheduler&amp;lt;/code&amp;gt; for Windows.&lt;br /&gt;
# Go to http://localhost:8080 in your browser and check out your fully functional Obsidian web application and scheduler! You can log in to the admin web application with the default user &#039;&#039;admin&#039;&#039; and password &#039;&#039;changeme&#039;&#039;.&lt;br /&gt;
# When you&#039;re done, stop Obsidian using the command: &amp;lt;code&amp;gt;./webObsidian.sh stop&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;webObsidian.bat stop&amp;lt;/code&amp;gt; for Windows.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; If you restart the Quick Start installation and see &amp;quot;lock wait timeout&amp;quot; or similar errors in the log screen, you may have to delete your embedded H2 database lock file. By default, the file name is &#039;&#039;obsidian.lock.db&#039;&#039; and is located in the user home directory.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; If you see a &amp;lt;code&amp;gt;java.net.BindException: Permission denied&amp;lt;/code&amp;gt; error on startup, you will need to use an alternate port. See [[Troubleshooting]] for details on how to change this.&lt;br /&gt;
&lt;br /&gt;
== Quick Method Using Existing Database ==&lt;br /&gt;
&lt;br /&gt;
If you wish to get Obsidian running quickly using a database platform you already have running, follow these steps:&lt;br /&gt;
&lt;br /&gt;
# Ensure you have a JDK (11 or up for Obsidian 6.x.x and 5.x.x, 1.7 or up for Obsidian 4.x.x) installed and that the &amp;lt;code&amp;gt;JAVA_HOME&amp;lt;/code&amp;gt; environment variable is set to your JDK installation directory.&lt;br /&gt;
# Unzip the Obsidian download zip file to a directory of your choice.&lt;br /&gt;
# At the command line, in the zip extraction directory, run the [[Installation_Guide|installer]] using this command, substituting the appropriate Obsidian version number: &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar&amp;lt;/code&amp;gt;&lt;br /&gt;
# Start Obsidian using the command: &amp;lt;code&amp;gt;./webObsidian.sh start scheduler&amp;lt;/code&amp;gt; for Linux or &amp;lt;code&amp;gt;webObsidian.bat start scheduler&amp;lt;/code&amp;gt; for Windows.&lt;br /&gt;
# Go to http://localhost:8080 in your browser and check out your fully functional Obsidian web application and scheduler! You can log in to the admin web application with the default user &#039;&#039;admin&#039;&#039; and password &#039;&#039;changeme&#039;&#039;.&lt;br /&gt;
# When you&#039;re done, stop Obsidian using the command: &amp;lt;code&amp;gt;./webObsidian.sh stop&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;webObsidian.bat stop&amp;lt;/code&amp;gt; for Windows.&lt;br /&gt;
&lt;br /&gt;
= Initial Setup  =&lt;br /&gt;
&lt;br /&gt;
This section covers the setup required after you&#039;ve selected your [[#Choose_Your_Installation_Type|installation type]]. &lt;br /&gt;
&lt;br /&gt;
== Database ==&lt;br /&gt;
&lt;br /&gt;
Obsidian requires a database which must be created before running Obsidian. Multiple [[#Supported Platforms|database platforms]] are supported. Obsidian can share a database/schema with another application.&lt;br /&gt;
&lt;br /&gt;
Note that the database must exist before deployment, but by default Obsidian will automatically create all required tables.&lt;br /&gt;
&lt;br /&gt;
Later, you will specify database connection parameters within Obsidian&#039;s installer to tell it how to connect. If you need to configure advanced settings or change settings directly after you&#039;ve run run the installer, see [[Advanced Configuration]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Obsidian needs to create the tables in the target database. If the schema is shared with your application’s tables, please ensure there are no name conflicts. If there are conflicts, separate schemas/databases can be used, or a table prefix can be specified as shown in [[Advanced_Configuration#Database_Properties|Advanced Configuration]]. [[Obsidian Tables]] lists the tables Obsidian will created upon first deployment.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Manual Schema Creation ===&lt;br /&gt;
&lt;br /&gt;
When you start Obsidian, it will automatically detect and create missing tables in your target database. However, if you wish to run Obsidian with a database user that does not have create privileges, or simply wish to prepare your database structure ahead of time, you can use the database scripts provided with your download zip file under the &amp;quot;db_scripts&amp;quot; directory.&lt;br /&gt;
&lt;br /&gt;
First create your database, and locate the &amp;quot;complete.sql&amp;quot; file under the directory for the database you are using under the &amp;quot;db_scripts&amp;quot; directory. Statements within this file contain the token &amp;quot;$px$&amp;quot; to enable table prefixes. Before you run the SQL file, replace all instances of the token &amp;quot;$px$&amp;quot; (without quotes) with a table prefix 6 characters or shorter (letters and underscores supported), or with blank.&lt;br /&gt;
&lt;br /&gt;
Then, in your SQL client of choice, execute the script and all the required Obsidian tables will be created.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Alternate Oracle Schemas ===&lt;br /&gt;
&lt;br /&gt;
After allowing Obsidian to create all the required tables and initial data by deploying it a single time, Obsidian can be run with an alternate Oracle user by specifying the target schema on Oracle databases. See [[Advanced_Configuration#Database_Properties|Advanced Configuration]] for the property to set.&lt;br /&gt;
&lt;br /&gt;
If you use a separate user from the schema owner, you must grant the user [[Obsidian_Tables#Oracle Privileges|certain privileges]] for Obsidian to function correctly.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== MS SQL Server Snapshot Isolation ===&lt;br /&gt;
&lt;br /&gt;
For maximum compatibility and to avoid deadlocks, MS SQL Server should be configured to use [https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql/snapshot-isolation-in-sql-server read committed snapshot isolation].&lt;br /&gt;
&lt;br /&gt;
This can be enabled on your database by running the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ALTER DATABASE MyDatabase SET ALLOW_SNAPSHOT_ISOLATION ON&lt;br /&gt;
ALTER DATABASE MyDatabase SET READ_COMMITTED_SNAPSHOT ON&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
&lt;br /&gt;
By default, Obsidian manages its own set users and logins to restrict access to the admin web application and REST API. A default “admin” user is created when the scheduler is first deployed and no additional setup is required.&lt;br /&gt;
&lt;br /&gt;
Alternatively, to use LDAP authentication, select the LDAP option in the installer and enter your LDAP details.&lt;br /&gt;
&lt;br /&gt;
As of 7.0.0, you can also select OAuth / OIDC or SAML 2.0 authentication to enable Single Sign-On via an external identity provider. See [[Authenticator#OAuthAuthenticator_(OIDC_SSO)|OAuthAuthenticator]] and [[Authenticator#SAMLAuthenticator_(SAML_2.0_SSO)|SAMLAuthenticator]] for details.&lt;br /&gt;
&lt;br /&gt;
Finally, you may implement your own [[authenticator|custom authenticator]] Java class, which you also enter when running the installer.&lt;br /&gt;
&lt;br /&gt;
= Running the Installer =&lt;br /&gt;
&lt;br /&gt;
Now that you&#039;ve chosen your installation type and performed initial setup of your database and authentication, you can run Obsidian&#039;s installer to configure and build a set of artifacts that you can either use to embed Obsidian, or deploy directly.&lt;br /&gt;
&lt;br /&gt;
To run the installer, please follow our detailed [[Installation_Guide|installation guide]].&lt;br /&gt;
&lt;br /&gt;
=== Installer-Created Artifacts ===&lt;br /&gt;
&lt;br /&gt;
Once you&#039;ve run the installer, your installation directory will look something like the following, depending on which artifacts you chose to generate.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
├───db_scripts&lt;br /&gt;
└───Obsidian-7.0.0&lt;br /&gt;
    │   com.carfey.yaml.reference&lt;br /&gt;
    │   obsidian.war&lt;br /&gt;
    │   obsidian-builtin-job-src.jar&lt;br /&gt;
    │   obsidianForkedJob.bat&lt;br /&gt;
    │   obsidianForkedJob.sh&lt;br /&gt;
    │   obsidian-javadoc.zip&lt;br /&gt;
    │   standaloneObsidian.bat&lt;br /&gt;
    │   standaloneObsidian.sh&lt;br /&gt;
    │   standaloneObsidianAdmin.war&lt;br /&gt;
    │   webObsidian.bat&lt;br /&gt;
    │   webObsidian.sh&lt;br /&gt;
    │&lt;br /&gt;
    ├───embed-tomcat-lib  (present when an Embedded Tomcat pack is selected)&lt;br /&gt;
    ├───license&lt;br /&gt;
    ├───linux             (present on Unix when an Embedded Tomcat pack is selected)&lt;br /&gt;
    ├───logs&lt;br /&gt;
    └───standalone&lt;br /&gt;
        (various JAR files...)&lt;br /&gt;
        obsidian-yaml-configuration.jar&lt;br /&gt;
        obsidian.jar&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The installer creates a top-level directory named &#039;&#039;&#039;Obsidian-n.n.n&#039;&#039;&#039; containing generated artifacts. The key files and directories are:&lt;br /&gt;
&lt;br /&gt;
; com.carfey.yaml.reference or com.carfey.properties.reference&lt;br /&gt;
: Copy of the configuration file generated by the installer.&lt;br /&gt;
; obsidian.war&lt;br /&gt;
: Combined scheduler and admin web application WAR. Deploy directly to a servlet container, or run locally using the Embedded Tomcat pack (see below). Customise by adding your job code and dependencies as JARs to &#039;&#039;&#039;/WEB-INF/lib&#039;&#039;&#039; within the archive.&lt;br /&gt;
; standaloneObsidian.bat and standaloneObsidian.sh&lt;br /&gt;
: Windows and Linux scripts to start the standalone scheduler process with no web application. Customise by adding your job code and dependencies as JARs to the &#039;&#039;&#039;standalone&#039;&#039;&#039; directory.&lt;br /&gt;
; webObsidian.bat and webObsidian.sh&lt;br /&gt;
: Windows and Linux scripts to start Obsidian using the embedded Tomcat runtime (requires the &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; or &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; pack). Use &#039;&#039;&#039;start scheduler&#039;&#039;&#039; to run the combined scheduler+admin WAR, or &#039;&#039;&#039;start adminOnly&#039;&#039;&#039; to run the standalone admin WAR. Stop with &#039;&#039;&#039;stop&#039;&#039;&#039;.&lt;br /&gt;
; standaloneObsidianAdmin.war&lt;br /&gt;
: Standalone admin web application WAR (no scheduler). Deploy directly to a servlet container or run via the embedded Tomcat pack.&lt;br /&gt;
; embed-tomcat-lib&lt;br /&gt;
: Present when an Embedded Tomcat pack is selected. Contains the embedded Tomcat runtime JARs used by the &#039;&#039;&#039;webObsidian&#039;&#039;&#039; scripts.&lt;br /&gt;
; linux&lt;br /&gt;
: Present on Unix when an Embedded Tomcat pack is selected. Optional user-level systemd unit and desktop-entry templates, plus &#039;&#039;&#039;install-embedded-linux-integration.sh&#039;&#039;&#039; and &#039;&#039;&#039;README-linux-integration.md&#039;&#039;&#039;. See [[#Running_Obsidian_as_a_Linux_user_service|Running Obsidian as a Linux user service]].&lt;br /&gt;
; standalone&lt;br /&gt;
: Directory containing JARs required to run the standalone scheduler process. &#039;&#039;&#039;obsidian-yaml-configuration.jar&#039;&#039;&#039; or &#039;&#039;&#039;obsidian-properties-configuration.jar&#039;&#039;&#039; contains the Obsidian configuration file. To make configuration changes for the standalone scheduler, update the configuration file within this JAR.&lt;br /&gt;
&lt;br /&gt;
= Starting Obsidian with embedded Tomcat =&lt;br /&gt;
&lt;br /&gt;
If you selected the &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; or &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; pack during installation, use the &#039;&#039;&#039;webObsidian&#039;&#039;&#039; scripts to start and stop Obsidian.&lt;br /&gt;
&lt;br /&gt;
From the Obsidian install directory:&lt;br /&gt;
&lt;br /&gt;
 webObsidian.bat start scheduler&lt;br /&gt;
 (Unix: ./webObsidian.sh start scheduler)&lt;br /&gt;
&lt;br /&gt;
For standalone admin only:&lt;br /&gt;
&lt;br /&gt;
 webObsidian.bat start adminOnly&lt;br /&gt;
&lt;br /&gt;
Stop:&lt;br /&gt;
&lt;br /&gt;
 webObsidian.bat stop&lt;br /&gt;
 (Unix: ./webObsidian.sh stop)&lt;br /&gt;
&lt;br /&gt;
Default HTTP port is &#039;&#039;&#039;8080&#039;&#039;&#039;. To change ports, pass &#039;&#039;&#039;-Dembedded.tomcat.http.port&#039;&#039;&#039;, &#039;&#039;&#039;-Dembedded.tomcat.stop.port&#039;&#039;&#039;, and &#039;&#039;&#039;-Dembedded.tomcat.stop.key&#039;&#039;&#039; as JVM arguments before starting.&lt;br /&gt;
&lt;br /&gt;
On Linux you can also run Obsidian as a user-level systemd service, or launch it from a desktop entry, instead of calling the script by hand — see [[#Running_Obsidian_as_a_Linux_user_service|Running Obsidian as a Linux user service]] below.&lt;br /&gt;
&lt;br /&gt;
If you are deploying &#039;&#039;&#039;obsidian.war&#039;&#039;&#039; to your own external servlet container instead, refer to your container&#039;s deployment documentation.&lt;br /&gt;
&lt;br /&gt;
= Running Obsidian as a Linux user service =&lt;br /&gt;
&lt;br /&gt;
On Linux, an embedded Tomcat pack can install a user-level systemd unit and a desktop entry — see [[Installation_Guide#Linux_desktop_and_systemd_integration|Linux desktop and systemd integration]] for the installer option and the manual &#039;&#039;&#039;linux/install-embedded-linux-integration.sh&#039;&#039;&#039; script. Installing those files does not enable or start anything, so enable the service yourself:&lt;br /&gt;
&lt;br /&gt;
 systemctl --user enable --now obsidian-embedded@scheduler.service&lt;br /&gt;
&lt;br /&gt;
Check and stop it with:&lt;br /&gt;
&lt;br /&gt;
 systemctl --user status obsidian-embedded@scheduler.service&lt;br /&gt;
 systemctl --user stop obsidian-embedded@scheduler.service&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;./webObsidian.sh stop&#039;&#039; also stops a service-started instance. Do &#039;&#039;&#039;not&#039;&#039;&#039; run &#039;&#039;./webObsidian.sh start scheduler&#039;&#039; by hand while the service is running — both bind the same HTTP and stop ports.&lt;br /&gt;
&lt;br /&gt;
== Ports ==&lt;br /&gt;
&lt;br /&gt;
The unit and the desktop entry both invoke &#039;&#039;&#039;webObsidian.sh&#039;&#039;&#039;, so they use the script&#039;s ports: HTTP &#039;&#039;&#039;8080&#039;&#039;&#039; and stop port &#039;&#039;&#039;9597&#039;&#039;&#039;. Passing &#039;&#039;-Dembedded.tomcat.*&#039;&#039; on a &#039;&#039;java&#039;&#039; command line has no effect on the service — edit the values in &#039;&#039;&#039;webObsidian.sh&#039;&#039;&#039; before enabling it.&lt;br /&gt;
&lt;br /&gt;
== Running without an active login session ==&lt;br /&gt;
&lt;br /&gt;
This is a &#039;&#039;&#039;user&#039;&#039;&#039; service, so by default it runs only while that user has a login session and stops at logout. To keep Obsidian running after logout and start it at boot, enable lingering for the account:&lt;br /&gt;
&lt;br /&gt;
 loginctl enable-linger &amp;lt;nowiki&amp;gt;&amp;lt;username&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you want Obsidian to run as a system service independent of any user account, write your own unit under &#039;&#039;/etc/systemd/system/&#039;&#039; using &#039;&#039;&#039;linux/obsidian-embedded@.service.in&#039;&#039;&#039; as a starting point.&lt;br /&gt;
&lt;br /&gt;
== Standalone admin installs ==&lt;br /&gt;
&lt;br /&gt;
The generated unit runs &#039;&#039;&#039;webObsidian.sh start scheduler&#039;&#039;&#039;. If you installed only the &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; pack, change &#039;&#039;ExecStart&#039;&#039; in &#039;&#039;~/.config/systemd/user/obsidian-embedded@scheduler.service&#039;&#039; to use &#039;&#039;&#039;start adminOnly&#039;&#039;&#039; before enabling the service.&lt;br /&gt;
&lt;br /&gt;
== Removing the integration ==&lt;br /&gt;
&lt;br /&gt;
 systemctl --user disable --now obsidian-embedded@scheduler.service&lt;br /&gt;
 rm ~/.config/systemd/user/obsidian-embedded@scheduler.service&lt;br /&gt;
 rm ~/.local/share/applications/obsidian-embedded.desktop&lt;br /&gt;
 systemctl --user daemon-reload&lt;br /&gt;
&lt;br /&gt;
The same details ship as &#039;&#039;&#039;linux/README-linux-integration.md&#039;&#039;&#039; in your install directory.&lt;br /&gt;
&lt;br /&gt;
= Embedding Obsidian &amp;amp; Customizing Artifacts =&lt;br /&gt;
&lt;br /&gt;
The Obsidian scheduler process needs Java classpath access to your jobs so it can detect valid jobs and execute them. This requires a bit of customization before you are ready to run custom Java jobs. If you still haven&#039;t created your custom jobs, consult [[Implementing Jobs]] before proceeding. If you only intend to use [[Scripting Jobs]], customization will not be required and you can skip to [[#Deployment|Deployment]].&lt;br /&gt;
&lt;br /&gt;
If you want to embed Obsidian into your application or need to deploy jobs and their dependent libraries to the generated Obsidian artifacts, follow the steps in the appropriate sections below.&lt;br /&gt;
&lt;br /&gt;
Note that only Obsidian scheduler processes need to be customized to include your job code. The admin web application itself does not require classpath access to your jobs.&lt;br /&gt;
&lt;br /&gt;
== Embedding Obsidian - Importing Libraries and Properties File ==&lt;br /&gt;
&lt;br /&gt;
To embed Obsidian, you will need to update your application build to bring in the JAR files it requires, along with the Obsidian properties file. Simply follow these steps:&lt;br /&gt;
&lt;br /&gt;
# Import all required [[Advanced_Configuration#Dependent_Libraries|Obsidian JAR files]] into your project and reference them in your build files. &lt;br /&gt;
#* These can be obtained from the &amp;lt;code&amp;gt;standalone&amp;lt;/code&amp;gt; directory of your installation. Make sure you &#039;&#039;&#039;exclude&#039;&#039;&#039; &amp;lt;code&amp;gt;obsidian-(yaml/properties)-configuration.jar&amp;lt;/code&amp;gt;&lt;br /&gt;
#* Ensure your Maven, Gradle or Ant files are updated to include all required libraries in your build, along with your IDE.&lt;br /&gt;
# Make a copy of &amp;lt;code&amp;gt;com.carfey.yaml.reference&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;com.carfey.properties.reference&amp;lt;/code&amp;gt; from your installation with the &amp;lt;code&amp;gt;.reference&amp;lt;/code&amp;gt; suffix removed, and add it to your project&#039;s classpath.&lt;br /&gt;
#* This is typically just a matter of adding it to your project&#039;s resources directory.&lt;br /&gt;
#* From now on, you can update this file to change the various configuration options such as database connection details.&lt;br /&gt;
#* You may wish to leave this file out of source control and let developers configure their own installations. However, any running Obsidian instance will need access to a valid [[Advanced_Configuration#Properties.2FYaml_File|properties file]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Maven users:&#039;&#039;&#039; Note that we do not publish Maven artifacts for Obsidian, so you will not be able to include them by referencing a public repository.&lt;br /&gt;
&lt;br /&gt;
== Adding Custom Jobs to Standalone Scheduler ==&lt;br /&gt;
&lt;br /&gt;
In order for the standalone scheduler to detect and execute custom jobs written in Java, you will need to ensure Obsidian&#039;s classpath contains the compiled job code, along with its dependencies.&lt;br /&gt;
&lt;br /&gt;
To do so, you need to perform just one step before starting the standalone scheduler:&lt;br /&gt;
&lt;br /&gt;
# Copy JAR files containing custom Obsidian jobs and all runtime dependencies to the &amp;lt;code&amp;gt;standalone&amp;lt;/code&amp;gt; directory in your installation.&lt;br /&gt;
#* These JARs should be alongside &amp;lt;code&amp;gt;obsidian.jar&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* All JAR files in this directory will be automatically added to the classpath when run via the &amp;lt;code&amp;gt;standaloneObsidian.sh&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;standaloneObsidian.bat&amp;lt;/code&amp;gt; scripts.&lt;br /&gt;
&lt;br /&gt;
== Adding Custom Jobs to Combined Scheduler and Admin Web Application ==&lt;br /&gt;
&lt;br /&gt;
In order for the combined scheduler and admin web application to detect and execute custom jobs written in Java, you will need to ensure Obsidian&#039;s classpath contains the compiled job code, along with its dependencies.&lt;br /&gt;
&lt;br /&gt;
To do so, you need to perform just one step before deploying the Obsidian WAR file:&lt;br /&gt;
&lt;br /&gt;
# Copy JAR files containing custom Obsidian jobs and all runtime dependencies to the &amp;lt;code&amp;gt;WEB-INF/lib&amp;lt;/code&amp;gt; directory within the WAR archive.&lt;br /&gt;
#* These JARs should be alongside &amp;lt;code&amp;gt;obsidian.jar&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* WAR files use the ZIP format and can be extracted and recompressed with normal ZIP tools or the JDK &amp;lt;code&amp;gt;jar&amp;lt;/code&amp;gt; utility.&lt;br /&gt;
&lt;br /&gt;
== Updating the Properties File in Admin Web Applications ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note that updating the WAR artifact is not necessary to perform properties file changes when using an external properties file as described [[Advanced_Configuration#Properties_File|here]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you need to change [[Advanced_Configuration#Properties_File|configuration properties]] for a standalone admin web application or combined scheduler and admin web application, follow these steps:&lt;br /&gt;
&lt;br /&gt;
# Obtain the WAR artifact from your installation that requires changes. This is either &amp;lt;code&amp;gt;obsidian.war&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;standaloneObsidianAdmin.war&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Extract the WAR file to a clean directory.&lt;br /&gt;
# Edit the &amp;lt;code&amp;gt;com.carfey.(yaml/properties)&amp;lt;/code&amp;gt; file within the subdirectory &amp;lt;code&amp;gt;WEB-INF/classes&amp;lt;/code&amp;gt; and save your changes.&lt;br /&gt;
# Rebundled the WAR artifact using a zip utility or the JDK &amp;lt;code&amp;gt;jar&amp;lt;/code&amp;gt; utility.&lt;br /&gt;
#* Some zip utilities such as 7Zip will allow you to edit files within an archive directly without the previous steps. It is generally safe to do so.&lt;br /&gt;
&lt;br /&gt;
= Deployment =&lt;br /&gt;
&lt;br /&gt;
You are now ready to deploy your scheduler and/or admin web application.&lt;br /&gt;
&lt;br /&gt;
== Embedded Scheduler ==&lt;br /&gt;
&lt;br /&gt;
When Obsidian is embedded, there is no separate deployment process. However, you will need to start and stop Obsidian with your application. If you are using Spring, we recommend you use our [[Spring Integration]] instead, which will automatically start and stop the scheduler for you.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
During your application startup, once it is fully initialized (or as close as possible), start Obsidian, and save a reference to the returned result so you can shut it down later:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// This will start the scheduler on the first call to get().&lt;br /&gt;
&lt;br /&gt;
com.carfey.ops.job.SchedulerStarter starter = com.carfey.ops.job.SchedulerStarter.get(com.carfey.ops.job.SchedulerStarter.SchedulerMode.EMBEDDED);&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
// Later, we need to gracefully shut down the scheduler &lt;br /&gt;
starter.shutDown();&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will run Obsidian&#039;s scheduler process, but will not include any of the web application or the REST API. This functionality is provided by the admin web application.&lt;br /&gt;
&lt;br /&gt;
== Standalone Admin Web Application ==&lt;br /&gt;
&lt;br /&gt;
To deploy the standalone admin web application, simply deploy the &amp;lt;code&amp;gt;standaloneObsidianAdmin.war&amp;lt;/code&amp;gt; file your servlet container of choice (e.g. Tomcat) after configuring it appropriately. You may rename the WAR file to have it deployed under a different context path, if desired (e.g. &amp;quot;ROOT.war&amp;quot; or &amp;quot;obsidian.war&amp;quot;). Consult your servlet documentation to find out how to deploy the application and start the servlet container.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; If you selected the &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; pack, you can use the &#039;&#039;&#039;webObsidian&#039;&#039;&#039; scripts included in your installation instead of deploying the WAR. This will make the application accessible at http://localhost:8080.&lt;br /&gt;
&lt;br /&gt;
* To start &lt;br /&gt;
** Linux: &amp;lt;code&amp;gt;./webObsidian.sh start adminOnly&amp;lt;/code&amp;gt;&lt;br /&gt;
** Windows: &amp;lt;code&amp;gt;webObsidian.bat start adminOnly&amp;lt;/code&amp;gt;&lt;br /&gt;
* To stop&lt;br /&gt;
** Linux: &amp;lt;code&amp;gt;./webObsidian.sh stop&amp;lt;/code&amp;gt; &lt;br /&gt;
** Windows: &amp;lt;code&amp;gt;webObsidian.bat stop&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Security Note:&#039;&#039;&#039; By default, the admin web application allows non-secure connections. If you wish to force secure connections through HTTPS, you can edit the web.xml in your war and uncomment the &amp;lt;security-constraint&amp;gt; element in the file. This will force all requests to redirect to an encrypted connection. For details on setting up SSL on your servlet container, refer to its documentation.&lt;br /&gt;
&lt;br /&gt;
== Standalone Scheduler ==&lt;br /&gt;
&lt;br /&gt;
To deploy and run the standalone scheduler, you may use the provided scripts in your installation directory:&lt;br /&gt;
* Linux: &amp;lt;code&amp;gt;./standaloneObsidian.sh start&amp;lt;/code&amp;gt;&lt;br /&gt;
* Windows: &amp;lt;code&amp;gt;standaloneObsidian.bat start&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You should always stop Obsidian gracefully when possible by using:&lt;br /&gt;
* Linux: &amp;lt;code&amp;gt;./standaloneObsidian.sh stop&amp;lt;/code&amp;gt;&lt;br /&gt;
* Windows: &amp;lt;code&amp;gt;standaloneObsidian.bat stop&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Both the start and stop commands may be supplied an additional argument to override the &amp;lt;code&amp;gt;listenerPort&amp;lt;/code&amp;gt; which defaults to 10451.&lt;br /&gt;
&lt;br /&gt;
Rather than use one of the provided scripts, you can invoke the equivalent Java command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
java com.carfey.ops.job.SchedulerStarter start &amp;lt;listenerPort&amp;gt;&lt;br /&gt;
java com.carfey.ops.job.SchedulerStarter stop &amp;lt;listenerPort&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Combined Scheduler and Admin Web Application ==&lt;br /&gt;
&lt;br /&gt;
To deploy the combined scheduler and admin web application, simply deploy the &amp;lt;code&amp;gt;obsidian.war&amp;lt;/code&amp;gt; file your servlet container of choice (e.g. Tomcat) after configuring it appropriately. You may rename the WAR file to have it deployed under a different context path, if desired (e.g. &amp;quot;ROOT.war&amp;quot;). Consult your servlet documentation to find out how to deploy the application and start the servlet container.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; If you selected the &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; pack, you can use the &#039;&#039;&#039;webObsidian&#039;&#039;&#039; scripts included in your installation instead of deploying the WAR. This will make the web application accessible at the URL http://localhost:8080.&lt;br /&gt;
&lt;br /&gt;
* To start &lt;br /&gt;
** Linux: &amp;lt;code&amp;gt;./webObsidian.sh start scheduler&amp;lt;/code&amp;gt;&lt;br /&gt;
** Windows: &amp;lt;code&amp;gt;webObsidian.bat start scheduler&amp;lt;/code&amp;gt;&lt;br /&gt;
* To stop&lt;br /&gt;
** Linux: &amp;lt;code&amp;gt;./webObsidian.sh stop&amp;lt;/code&amp;gt; &lt;br /&gt;
** Windows: &amp;lt;code&amp;gt;webObsidian.bat stop&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Security Note:&#039;&#039;&#039; By default, the admin web application allows non-secure connections. If you wish to force secure connections through HTTPS, you can edit the web.xml in your war and uncomment the &amp;lt;security-constraint&amp;gt; element in the file. This will force all requests to redirect to an encrypted connection. For details on setting up SSL on your servlet container, refer to its documentation.&lt;br /&gt;
&lt;br /&gt;
== Embedding the Obsidian Web Application in Another Web Application ==&lt;br /&gt;
&lt;br /&gt;
We do not generally recommend attempting to merge the Obsidian admin web application with another servlet web application, but it can be done by following these steps:&lt;br /&gt;
&lt;br /&gt;
To embed the full Obsidian web application in an existing web application, you will need to extract the necessary sections from the &amp;lt;code&amp;gt;WEB-INF/web.xml&amp;lt;/code&amp;gt; file in the &amp;lt;code&amp;gt;obsidian.war&amp;lt;/code&amp;gt; file and merge them into your application&#039;s &amp;lt;code&amp;gt;web.xml&amp;lt;/code&amp;gt; file. This includes all &amp;quot;listener&amp;quot;, &amp;quot;servlet&amp;quot;, &amp;quot;servlet-mapping&amp;quot;, &amp;quot;jsp-config&amp;quot;, &amp;quot;filter&amp;quot;, &amp;quot;filter-mapping&amp;quot; elements, and optionally &amp;quot;welcome-file-list&amp;quot; and &amp;quot;error-page&amp;quot; elements. Ensure that all servlet paths are maintained. If you are using a different servlet specification version, you may need to update the mappings to the appropriate format.&lt;br /&gt;
&lt;br /&gt;
Note the use of our &amp;lt;code&amp;gt;com.carfey.ops.servlet.StartupShutdownListener&amp;lt;/code&amp;gt; which takes care of starting up the scheduler instance and using the web container&#039;s default shutdown mechanism to ensure graceful shutdown. This listener will start Obsidian&#039;s scheduler process and initialize the Obsidian web application. &lt;br /&gt;
&lt;br /&gt;
If you wish to embed Obsidian&#039;s web application without the scheduler running, include the following parameter element in your &amp;lt;code&amp;gt;web.xml&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;context-param&amp;gt;&lt;br /&gt;
   &amp;lt;param-name&amp;gt;schedulerEnabled&amp;lt;/param-name&amp;gt;&lt;br /&gt;
   &amp;lt;param-value&amp;gt;false&amp;lt;/param-value&amp;gt;&lt;br /&gt;
&amp;lt;/context-param&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Additional Deployment Details ==&lt;br /&gt;
&lt;br /&gt;
=== Classpath Notes ===&lt;br /&gt;
&lt;br /&gt;
To run a scheduler with your custom code and jobs, you need to ensure the scheduler process classpath includes your code packaged into JAR files, as described [[#Embedding_Obsidian_.26_Customizing_Artifacts|above]]. &lt;br /&gt;
&lt;br /&gt;
If a compiled job is updated, you will have to restart your application or the servlet container after deploying updated jars, unless you are using [[Job Forking]].&lt;br /&gt;
&lt;br /&gt;
=== Setting Host Names ===&lt;br /&gt;
Obsidian instances will automatically assign themselves host names if no host name is explicitly set, but you may wish to give them explicit names to make scheduling and monitoring simpler.&lt;br /&gt;
&lt;br /&gt;
If you wish to assign explicit names, simply set the Java system property &amp;lt;code&amp;gt;schedulerDesignation&amp;lt;/code&amp;gt; to the host name of your choice. For example, if starting an instance using the standalone scheduler using &amp;lt;code&amp;gt;java&amp;lt;/code&amp;gt; directly, simply add the value &amp;lt;code&amp;gt;-DschedulerDesignation=myHostName&amp;lt;/code&amp;gt; to the end of the command.&lt;br /&gt;
&lt;br /&gt;
You may also use a properties file setting for the host name as detailed in [[Advanced Configuration]].&lt;br /&gt;
&lt;br /&gt;
=== Disabling Automatic Database Updates ===&lt;br /&gt;
&lt;br /&gt;
Obsidian automatically applies schema updates and data upgrades to its database on startup. In some cases, it may be desirable to disable this once the database has been fully initialized and upgrades to new versions are not going to be deployed. For example, you may wish to run Obsidian with a user who does not have privileges to modify the database schema.&lt;br /&gt;
&lt;br /&gt;
To do so, you can either set a Java system property, or if you are using the admin web application WAR, you can add a setting to your &amp;lt;code&amp;gt;WEB-INF/web.xml&amp;lt;/code&amp;gt; within the archive.&lt;br /&gt;
&lt;br /&gt;
As a system property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-DstartupRunnerClass=com.carfey.ops.run.NullRunner&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;WEB-INF/web.xml&amp;lt;/code&amp;gt;, after any &amp;lt;code&amp;gt;listener&amp;lt;/code&amp;gt; elements:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;context-param&amp;gt;&lt;br /&gt;
    &amp;lt;param-name&amp;gt;startupRunnerClass&amp;lt;/param-name&amp;gt;&lt;br /&gt;
    &amp;lt;param-value&amp;gt;com.carfey.ops.run.NullRunner&amp;lt;/param-value&amp;gt;&lt;br /&gt;
&amp;lt;/context-param&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Disabling DDL Updates ===&lt;br /&gt;
&lt;br /&gt;
It may also be helpful to apply schema updates (DDL) outside Obsidian through a privileged account, but allow Obsidian upgrades to make the necessary data initialization/modifications. This can be done using a &#039;&#039;&#039;skipDDL&#039;&#039;&#039; configuration property documented under [[Advanced_Configuration#Miscellaneous_Properties | Advanced Configuration]].&lt;br /&gt;
&lt;br /&gt;
=== Disabling Job Scheduling in the Web Application ===&lt;br /&gt;
&lt;br /&gt;
If you have combined scheduler and admin web application WAR already built, you can easily tweak it to disable the scheduler process. &lt;br /&gt;
&lt;br /&gt;
Simply add the following to your &amp;lt;code&amp;gt;WEB-INF/web.xml&amp;lt;/code&amp;gt; within the WAR archive after any &amp;lt;code&amp;gt;listener&amp;lt;/code&amp;gt; elements:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;context-param&amp;gt;&lt;br /&gt;
    &amp;lt;param-name&amp;gt;schedulerEnabled&amp;lt;/param-name&amp;gt;&lt;br /&gt;
    &amp;lt;param-value&amp;gt;false&amp;lt;/param-value&amp;gt;&lt;br /&gt;
&amp;lt;/context-param&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= You’re Good to Go! =&lt;br /&gt;
&lt;br /&gt;
You&#039;re now ready to log into the web application and start scheduling jobs! The URL you use to access Obsidian will depend on how it&#039;s been deployed, but will typically be something like http://localhost/obsidian, or http://localhost/standadminObsidianAdmin.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Default User:&#039;&#039;&#039; Each Obsidian installation using native authentication starts with a single default user named &#039;&#039;admin&#039;&#039; with password &#039;&#039;changeme&#039;&#039;. You should change this after your first log in. See [[Admin_User_Management|User Management]] for how to change a user&#039;s password.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
At this point we suggest you play around with the admin web application. Many screens offer inline help. Otherwise, you can refer to our [[Admin Web Application Guide]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To get started writing jobs that you can run in Obsidian, see [[Implementing_Jobs|Implementing Jobs]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
See the &#039;&#039;&#039;[[User Guide]]&#039;&#039;&#039; if you have questions or wish to explore what Obsidian offers.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Upgrading_Obsidian&amp;diff=4162</id>
		<title>Upgrading Obsidian</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Upgrading_Obsidian&amp;diff=4162"/>
		<updated>2026-07-31T15:02:38Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;There are a number of options available to you. Select the one that best suits your situation.&lt;br /&gt;
&lt;br /&gt;
Note that you don&#039;t necessarily need to go through the installer UI again, although you&#039;re free to do so. Since the primary purpose of the installer is to provide fully configured Obsidian artifacts and your environment would already be configured, you really just need the updated artifacts (libraries and WAR files). Even if you want to take advantage of new configuration options, you could use the [[Advanced_Configuration|Configuration Reference]] to make the necessary changes.&lt;br /&gt;
&lt;br /&gt;
=== Upgrading to 7.0.0 ===&lt;br /&gt;
&lt;br /&gt;
==== Remember Me removal ====&lt;br /&gt;
&lt;br /&gt;
The Remember Me feature has been removed. No operator action is required. On first startup after the upgrade, &#039;&#039;Release7Dot0Dot0Upgrader&#039;&#039; runs automatically and drops the &#039;&#039;USER_COOKIE&#039;&#039; table (if it exists) and removes the &#039;&#039;allowRememberMe&#039;&#039; system parameter. Users who had active Remember Me sessions will be prompted to log in on their next visit.&lt;br /&gt;
&lt;br /&gt;
==== OAuth / OIDC authentication (optional) ====&lt;br /&gt;
&lt;br /&gt;
Obsidian 7.0.0 adds an optional OAuth / OIDC authenticator. Existing installations continue to use their current authenticator (DB or LDAP) with no changes required. To switch to OAuth / OIDC: configure &#039;&#039;OAuthAuthenticator&#039;&#039; properties in the Obsidian configuration file (&#039;&#039;com.carfey.properties&#039;&#039; or YAML equivalent) — see [[Authenticator#OAuthAuthenticator_(OIDC_SSO)]] and [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties]] — then restart Obsidian.&lt;br /&gt;
&lt;br /&gt;
==== Migrating saved automated install XML (Obsidian 6 → 7) ====&lt;br /&gt;
&lt;br /&gt;
If you use headless / automated install with an XML file saved from Obsidian 6 (IzPack 4.3.5), you must migrate that file before installing with Obsidian 7. Unzip the Obsidian 7.0.0 download and run the migrate helper (Windows: &#039;&#039;&#039;migrate-headless.bat --in my-old-config.xml --out my-config-izpack5.xml&#039;&#039;&#039;, Unix: &#039;&#039;&#039;migrate-headless.sh&#039;&#039;&#039; with the same arguments, or &#039;&#039;&#039;java -jar Obsidian-Install-Migrate.jar --in my-old-config.xml --out my-config-izpack5.xml&#039;&#039;&#039;). Review &#039;&#039;&#039;my-config-izpack5.xml.migration-report.txt&#039;&#039;&#039; only if the tool creates one — if it lists unknown panels or keys, contact [[Contact_the_Obsidian_Scheduler_Team|Carfey support]]. Then install with the migrated file: &#039;&#039;&#039;java -jar Obsidian-Install-7.0.0.jar my-config-izpack5.xml&#039;&#039;&#039;. The migrate helper covers Obsidian&#039;s standard installer panels only and does not modify product installer descriptors or custom installers.&lt;br /&gt;
&lt;br /&gt;
==== Upgrading evaluation installs (Jetty quick start → embedded Tomcat) ====&lt;br /&gt;
&lt;br /&gt;
Obsidian 7.0.0 removes the Jetty quick-start pack and &#039;&#039;&#039;jetty-home&#039;&#039;&#039; distribution. If you previously installed with a Jetty-based quick start or embedded Jetty pack:&lt;br /&gt;
&lt;br /&gt;
# Re-run the installer or edit your automated install XML to select &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; (scheduler) or &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; (admin), &#039;&#039;&#039;and&#039;&#039;&#039; the matching WAR pack.&lt;br /&gt;
# Remove references to legacy Jetty pack names in any saved automated install XML. If the file is still IzPack 4 format, run the migrate helper first (see [[Upgrading_Obsidian#Migrating_saved_automated_install_XML_.28Obsidian_6_.E2.86.92_7.29|Migrating saved automated install XML]]).&lt;br /&gt;
# After install, use &#039;&#039;&#039;webObsidian start scheduler&#039;&#039;&#039; (or &#039;&#039;&#039;start adminOnly&#039;&#039;&#039;) instead of the Jetty &#039;&#039;&#039;start.jar&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
If the migrate helper produces a &#039;&#039;&#039;.migration-report.txt&#039;&#039;&#039; file, review it before running the install — it will flag any pack renames that need attention.&lt;br /&gt;
&lt;br /&gt;
==== Installer breaking changes (Obsidian 6 → 7) ====&lt;br /&gt;
&lt;br /&gt;
* Headless automated install XML must use IzPack 5 panel class names and &#039;&#039;id&#039;&#039; attributes — migrate saved Obsidian 6 files with the tooling above.&lt;br /&gt;
* GUI installer — same configuration panels and outcomes; underlying engine is IzPack 5.&lt;br /&gt;
* Release zip — adds migrate JAR/scripts.&lt;br /&gt;
* Groovy and Flexmark libraries are now modular — see [[Advanced_Configuration#Dependent_Libraries]].&lt;br /&gt;
&lt;br /&gt;
== Automated Installer File ==&lt;br /&gt;
This section applies if you have saved an automated installer XML file from a previous version of Obsidian.&lt;br /&gt;
&lt;br /&gt;
If the release notes do not document any installer incompatibility between source and target releases, this means there are no new configuration options available and you can use your automated installer file without modification.  &lt;br /&gt;
&lt;br /&gt;
You can see [[Installation_Guide#Completing_the_Installation|installation configuration]] for more information, but essentially all you need to do is run &amp;lt;code&amp;gt;java -jar Obsidian-Install-n-n-n.jar my-obsidian-install-config.xml&amp;lt;/code&amp;gt;. You may wish to change the target installation path before proceeding.&lt;br /&gt;
&lt;br /&gt;
If the release notes do document installer incompatibility between releases, follow the relevant instructions to update your installer configuration file and then follow the same procedure.&lt;br /&gt;
&lt;br /&gt;
== Existing Configuration ==&lt;br /&gt;
&lt;br /&gt;
You have a choice when it comes to upgrading an Obsidian environment that is already configured.&lt;br /&gt;
&lt;br /&gt;
# You can rerun the installer UI to get the configured artifacts.&lt;br /&gt;
# Use the [[Installation_Guide#Running_the_Obsidian_Installer|quickstart]] installation mode to get the updated artifacts with the quickstart configuration.&lt;br /&gt;
&lt;br /&gt;
==== Existing Configuration with QuickStart ====&lt;br /&gt;
If you are using [[Advanced_Configuration#Properties.2FYaml_File|overrides]] support to specify &#039;&#039;&#039;ALL&#039;&#039;&#039; your configuration items, you likely are ready to go as the default configuration items are being overridden.&lt;br /&gt;
&lt;br /&gt;
Otherwise, you are relying at least partially on embedded configuration items. You will need to replace the quickstart configuration values with your existing configuration values. The Obsidian WAR files have the &amp;lt;code&amp;gt;com.carfey.(properties/yaml)&amp;lt;/code&amp;gt; file inside at &amp;lt;code&amp;gt;WEB-INF/classes/com.carfey.(properties/yaml)&amp;lt;/code&amp;gt; and the standalone installation has it at the root of &#039;&#039;obsidian-properties-configuration.jar&#039;&#039; or &#039;&#039;obsidian-yaml-configuration.jar&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Performing the Upgrade ==&lt;br /&gt;
We highly recommend performing a database backup and an installation backup (filesystem backup) prior to performing the upgrade with rollback strategies in place to restore the previous installation with its data.&lt;br /&gt;
&lt;br /&gt;
Obsidian automatically applies any database modifications during the first deployment of the new version.&lt;br /&gt;
&lt;br /&gt;
The simplest mechanism is to bring down all your Obsidian Scheduler nodes during a quiet scheduling period. Deploy the new artifacts and start the each node individually verifying its startup before proceeding to the next node.&lt;br /&gt;
&lt;br /&gt;
== Database Upgrade Scripts ==&lt;br /&gt;
&lt;br /&gt;
When you start Obsidian, it will automatically upgrade the schema in your target database. However, if you wish to run Obsidian with a database user without create and drop privileges, or simply wish to prepare your database structure ahead of time, you can use the database upgrade scripts provided with your download zip file under the &amp;quot;db_scripts&amp;quot; directory.&lt;br /&gt;
&lt;br /&gt;
First locate all files named &amp;quot;*upgrade.sql&amp;quot; under the directory for the database you are using under the &amp;quot;db_scripts&amp;quot; directory. Statements within these files contain the token &amp;quot;$px$&amp;quot; to enable table prefixes. Before you run the SQL files, replace all instances of the token &amp;quot;$px$&amp;quot; (without quotes) with a table prefix 6 characters or shorter (letters and underscores supported), or with blank.&lt;br /&gt;
&lt;br /&gt;
Then, in your SQL client of choice, execute the scripts in order of their version numbers. This will upgrade your schema to your target version.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to allow the data portion of the migrations to execute while preventing the DDL scripts to run. To do this, you need to [[Getting_Started#Disabling_DDL_Updates|disable DDL Updates]].&lt;br /&gt;
&lt;br /&gt;
== Performing an Always On Cluster Upgrade ==&lt;br /&gt;
&lt;br /&gt;
If you absolutely must maintain at least one node running, Obsidian should be able to continue scheduling and executing on a single node while the other(s) is/are upgraded although we make no assurances and recommend that you &#039;&#039;&#039;test out your specific upgrade scenario&#039;&#039;&#039; first. Depending on the content between source and target releases, we also recommend that you don&#039;t attempt to perform other changes such as job configuration changes, failure resubmissions, etc. during this time. Feel free to [[Contact_the_Obsidian_Scheduler_Team|contact us]] with questions about your specific upgrade scenario.&lt;br /&gt;
&lt;br /&gt;
===== Always On Cluster Upgrade Steps =====&lt;br /&gt;
# Bring down all but one Obsidian Scheduler node. &lt;br /&gt;
# Target a downed node for upgrade.&lt;br /&gt;
# Deploy the upgraded artifacts to the targeted downed node.&lt;br /&gt;
# Startup the upgraded node.&lt;br /&gt;
# Verify successful startup.&lt;br /&gt;
# Bring down the still-running older Obsidian Scheduler node.&lt;br /&gt;
# Repeat steps 3-5 for each remaining downed node in the cluster.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Troubleshooting&amp;diff=4161</id>
		<title>Troubleshooting</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Troubleshooting&amp;diff=4161"/>
		<updated>2026-07-31T15:02:30Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__FORCETOC__&lt;br /&gt;
The following are steps to troubleshooting common problems users have had in Obsidian. If you don&#039;t see your problem listed, [[Contact_Carfey_Software|contact us]] or use our Free Live Chat Support link on the left.&lt;br /&gt;
&lt;br /&gt;
= My job isn&#039;t running when expected. Why not? =&lt;br /&gt;
This may be a case where your schedule isn&#039;t correct for the execution times you want, or the job may be failing.&lt;br /&gt;
&lt;br /&gt;
# First, you should confirm that your job isn&#039;t actually running. You can check for the [[Admin_Job_Activity|Job Activity]] screen to see if there are any scheduled or completed jobs. You may see that a job failed; to see details on it, simply expand the row to see an exception stack trace and the option to resubmit the job. If your job is failing, you may wish to set up [[Admin_Notifications|notifications]] so that you know of future problems quickly.&lt;br /&gt;
# If you don&#039;t see any jobs listed there, you can confirm what execution times your schedule will result in. To see what times your job will execute, check the [[Admin_Job_Run_Time_Preview|Run Time Preview]] screen.&lt;br /&gt;
# Next, you can check for any general server health issues. To do so, first you can search the [[Admin_Logs|Logs]] screen for any errors or warnings that may indicate a server issue.&lt;br /&gt;
# Finally, check your application logs (e.g. Tomcat stdout logs, etc.). If there are database issues, these logs may be the only place where server issues are logged.&lt;br /&gt;
# If all else fails, [[Contact_Carfey_Software|contact us]] or use our Free Live Chat Support link on the left.&lt;br /&gt;
&lt;br /&gt;
= When I try to enter my job&#039;s class name in the UI, I get the message &amp;quot;Could not locate the class [MyJob]&amp;quot;. What&#039;s the problem? =&lt;br /&gt;
&lt;br /&gt;
This problem is generally caused by one of these issues:&lt;br /&gt;
&lt;br /&gt;
# If you are running a combined scheduler and web application, the JAR file containing the job is not on the classpath of the servlet container (Tomcat/Jetty/etc). JARs are placed under &amp;lt;code&amp;gt;WEB-INF/lib&amp;lt;/code&amp;gt; in the web application. &lt;br /&gt;
# If you are running a standalone web application with no scheduler running, your other Obsidian instance which includes the scheduler has not had its [[Implementing_Jobs#Classpath_Scanning|Classpath Scanning]] configuration set, or has not been started with the appropriate classpath required for it to save the job metadata the web application uses to validate jobs. See [[Implementing_Jobs#Classpath_for_Building|Classpath for Building]].&lt;br /&gt;
# Obsidian has not been restarted after updating the classpath with a new JAR.&lt;br /&gt;
# Job does not implement &amp;lt;code&amp;gt;com.carfey.ops.job.SchedulableJob&amp;lt;/code&amp;gt;. &lt;br /&gt;
# Job dependencies (i.e. other JARs or classes) are not available on the classpath. These must be deployed with the job.&lt;br /&gt;
# User has entered the job class with leading or trailing whitespace, or the job class has been misspelled.&lt;br /&gt;
# User did not enter the fully qualified class name (e.g. MyJob instead of com.company.MyJob).&lt;br /&gt;
&lt;br /&gt;
Just a note, you can use the [[Implementing_Jobs#Classpath_Scanning|Classpath Scanning]] feature to automatically find your jobs so you won&#039;t need to type them in as they&#039;ll be added to the list of available jobs.&lt;br /&gt;
&lt;br /&gt;
= Why isn&#039;t my job showing up? =&lt;br /&gt;
&lt;br /&gt;
Out of the box, Obsidian will display any previously scheduled jobs and Obsidian&#039;s bundled jobs. You can type in your class name as described in  [[Admin_Jobs#Nickname.2C_Class_and_Folder|Admin Jobs]]. &lt;br /&gt;
&lt;br /&gt;
If you are using Obsidian&#039;s [[Implementing_Jobs#Classpath_Scanning|classpath scanning]] and don&#039;t see your job pre-populated, make sure you set appropriate the &amp;quot;packageScannerPrefix&amp;quot; value in the [[Admin Scheduler Settings|scheduler settings]] screen, and ensure the user account running Obsidian has read privileges to the &amp;lt;code&amp;gt;WEB-INF&amp;lt;/code&amp;gt; directory of Obsidian&#039;s deployment directory.&lt;br /&gt;
&lt;br /&gt;
In addition, some users have had issues with classpath scanning not locating jobs in JARs created by tools which don&#039;t include directory entries, such as Eclipse. Scanning relies on these directory entries, so please ensure they are included. For example, in Eclipse, if you choose to export a JAR, make sure the &#039;&#039;Add Directory Entries&#039;&#039; option is selected.&lt;br /&gt;
&lt;br /&gt;
= I&#039;m trying to start Obsidian using the Quick Start method, but I&#039;m seeing &amp;quot;java.net.BindException&amp;quot;. =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
java.net.BindException: Permission denied&lt;br /&gt;
at java.net.PlainSocketImpl.socketBind(Native Method)&lt;br /&gt;
at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:383)&lt;br /&gt;
at java.net.ServerSocket.bind(ServerSocket.java:328)&lt;br /&gt;
at java.net.ServerSocket.&amp;lt;init&amp;gt;(ServerSocket.java:194)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is a common error on *nix platforms, including OS X. On these platforms, ports 0-1023 are privileged. By default, only the root user will be able to bind to them. Obsidian&#039;s quick start uses port 8080 by default; the error also occurs if that port is already in use.&lt;br /&gt;
&lt;br /&gt;
Changing the port as described below will fix the issue.&lt;br /&gt;
&lt;br /&gt;
To resolve this, you have two options:&lt;br /&gt;
# Use the &amp;quot;sudo&amp;quot; command to elevate your privileges to bind on a privileged port.&lt;br /&gt;
# Change the port you use. &lt;br /&gt;
&lt;br /&gt;
To change the port, edit the &amp;lt;code&amp;gt;-Dembedded.tomcat.http.port&amp;lt;/code&amp;gt; value in &amp;lt;code&amp;gt;webObsidian.sh&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;webObsidian.bat&amp;lt;/code&amp;gt;, or pass it as a JVM argument when starting Obsidian. The companion &amp;lt;code&amp;gt;-Dembedded.tomcat.stop.port&amp;lt;/code&amp;gt; (default 9597) must also be free. If you run the Linux user systemd service, edit the script rather than the unit — the unit invokes the script. See [[Getting_Started#Starting_Obsidian_with_embedded_Tomcat|Starting Obsidian with embedded Tomcat]].&lt;br /&gt;
&lt;br /&gt;
On Obsidian 5.x and 6.x, which bundled Jetty for the quick start instead of embedded Tomcat, change the port in the Jetty configuration or in the same &amp;lt;code&amp;gt;webObsidian&amp;lt;/code&amp;gt; scripts.&lt;br /&gt;
&lt;br /&gt;
= I&#039;m trying to start Obsidian on Windows using the Quick Start method, but a window appears and then disappears quickly. What&#039;s going on? =&lt;br /&gt;
&lt;br /&gt;
The quick start method runs Obsidian in a separate process to ensure you can gracefully stop it. Unfortunately, the separate process is failing before you can see what the problem is. To temporarily see the error and have it try to run in the same process, open the script in a text editor and find the line that launches Java: in &amp;lt;code&amp;gt;webObsidian.bat&amp;lt;/code&amp;gt; it begins with &amp;lt;code&amp;gt;start &amp;quot;Obsidian&amp;quot; java&amp;lt;/code&amp;gt;, and in &amp;lt;code&amp;gt;standaloneObsidian.bat&amp;lt;/code&amp;gt; it begins with &amp;lt;code&amp;gt;start java&amp;lt;/code&amp;gt;. Remove the &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; command and its window title if present (so the line begins with &amp;lt;code&amp;gt;java&amp;lt;/code&amp;gt;), save the file and try again. You should get an informative message. Correct the underlying cause, restore the original line and run it again.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= My Obsidian systemd service stops when I log out of Linux =&lt;br /&gt;
&lt;br /&gt;
The unit installed by &amp;lt;code&amp;gt;linux/install-embedded-linux-integration.sh&amp;lt;/code&amp;gt; is a &#039;&#039;&#039;user&#039;&#039;&#039; service, so systemd stops it when the user&#039;s last session ends and does not start it at boot. Enable lingering for the account:&lt;br /&gt;
&lt;br /&gt;
 loginctl enable-linger &amp;lt;nowiki&amp;gt;&amp;lt;username&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then confirm the service is enabled:&lt;br /&gt;
&lt;br /&gt;
 systemctl --user is-enabled obsidian-embedded@scheduler.service&lt;br /&gt;
&lt;br /&gt;
If the service must run independently of any user account, create your own unit under &amp;lt;code&amp;gt;/etc/systemd/system/&amp;lt;/code&amp;gt; based on &amp;lt;code&amp;gt;linux/obsidian-embedded@.service.in&amp;lt;/code&amp;gt; in your install directory. See [[Getting_Started#Running_Obsidian_as_a_Linux_user_service|Running Obsidian as a Linux user service]].&lt;br /&gt;
&lt;br /&gt;
= I tried running Obsidian in Jetty, but encountered the error &amp;quot;There is an error in invoking javac.  A full JDK (not just JRE) is required&amp;quot; =&lt;br /&gt;
&lt;br /&gt;
This applies to Obsidian 4.x through 6.x deployed to your own Jetty installation. It is caused by Jetty wanting a JDK for JSP compilation when it isn&#039;t configured.&lt;br /&gt;
&lt;br /&gt;
The simplest way to resolve this is to uncomment the following line from &amp;lt;code&amp;gt;start.ini&amp;lt;/code&amp;gt; in your Jetty installation by removing the hash sign:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# -Dorg.apache.jasper.compiler.disablejsr199=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Obsidian 7.0.0 is tested on Tomcat 10.x and its WARs contain no JSPs, so no JSP compilation — and therefore no JDK — is required.&lt;br /&gt;
&lt;br /&gt;
= My jobs aren&#039;t running and I&#039;m seeing this error: &amp;quot;Licence was refused: Licence is valid but no keys are available for lease. Are you running too many nodes?&amp;quot; =&lt;br /&gt;
&lt;br /&gt;
This can be caused by a few issues, but if you are unsure, [[Contact_the_Obsidian_Scheduler_Team|contact us]]:&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; You are running more Obsidian instances that you have available nodes on your license key. If you are running two nodes using the year-long free clustering node, your year may have run out.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Obsidian is unable to refresh your license lease properly, resulting in a new lease being acquired each time it contacts our license servers. This means to our servers it looks like multiple instances are trying to run concurrently. Ensure that Obsidian has access to read and write the Java temp directory (&amp;lt;code&amp;gt;java.io.tmpdir&amp;lt;/code&amp;gt;), since it requires this to track the license it has leased. Below is a sample of granting access to the temp directory on WebSphere Application Server 8.5: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Update policy file at: C:/WebSphere/AppServer/profiles/AppSrv01/properties/server.policy&lt;br /&gt;
&lt;br /&gt;
grant codeBase &amp;quot;file:${user.install.root}/installedApps/Cell01/obsidian_war.ear/-&amp;quot; {&lt;br /&gt;
&lt;br /&gt;
permission java.io.FilePermission &amp;quot;${java.io.tmpdir}${/}-&amp;quot;, &amp;quot;read,write&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Obsidian was not shut down properly, and was started in another location before the old license lease expired. Obsidian works by leasing a license for a specified amount of time, and refreshing it before it expires. When shut down gracefully, Obsidian will release this lease and make it available to any other node using the same key. If it is shut down non-gracefully, the lease will stay active, and only the same server will be able to refresh that same license before it expires. By default, a lease lasts an hour or two to gracefully handle sporadic connectivity.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; You are using the wrong license key. Check the license key set in the [[Admin Scheduler Settings|scheduler settings]] screen of the web application. This is the value Obsidian is using to verify its licensing status. Note that the value in your properties file is only used to initialize the Obsidian database, so once it is set, you must update the value in the [[Admin Scheduler Settings|scheduler settings]] screen.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; You may have runaway background Obsidian processes which are using a license that you are unaware of.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= I&#039;m getting a &amp;quot;Lock wait timeout exception&amp;quot;. What&#039;s going on? =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction&lt;br /&gt;
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1055)&lt;br /&gt;
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)&lt;br /&gt;
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3491)&lt;br /&gt;
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3423)&lt;br /&gt;
        at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1936)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you encounter an error like the one above and are using MySQL (particularly version 5.0), there is a chance you are hitting a [https://bugs.mysql.com/bug.php?id=20932 MySQL bug]. This problem seems to occur mainly on new Obsidian instances, so once corrected, there is a good chance you will never see the issue again.&lt;br /&gt;
&lt;br /&gt;
Two approaches have been used with success:&lt;br /&gt;
# In MySQL&#039;s my.cnf file, temporarily change (or add) the following line, and then restart mysqld: &amp;lt;pre&amp;gt;innodb_lock_wait_timeout = 600&amp;lt;/pre&amp;gt;&amp;lt;br /&amp;gt;This appears to only be an issue when Obsidian first gets running, and it can typically be reverted after a while.&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
# At a MySQL prompt, run the following command: &amp;lt;pre&amp;gt;optimize table semaphore;&amp;lt;/pre&amp;gt;&amp;lt;br /&amp;gt; If this resolves your issue, we suggest optimizing all your tables using [https://dev.mysql.com/doc/refman/8.0/en/mysqlcheck.html mysqlcheck].&lt;br /&gt;
&lt;br /&gt;
= I am running Obsidian on Oracle but I&#039;m seeing the error &amp;quot;ORA-00942: table or view does not exist&amp;quot; =&lt;br /&gt;
&lt;br /&gt;
This is usually caused by not setting the proper schema value for the Obsidian database user. &lt;br /&gt;
&lt;br /&gt;
This is easily resolved by setting the property &amp;lt;code&amp;gt;com.carfey.obsidian.db.schema&amp;lt;/code&amp;gt; to the name of your user (or other target schema). See [[Advanced Configuration]] for more details on this property.&lt;br /&gt;
&lt;br /&gt;
Example: &amp;lt;code&amp;gt;com.carfey.obsidian.db.schema=obsidian&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= I&#039;m seeing an error in the logs like &amp;quot;Maximum X connections already created for pool&amp;quot;. What&#039;s going on? =&lt;br /&gt;
&lt;br /&gt;
Obsidian uses an internal database connection pool to avoid opening connections more than necessary, which is an expensive operation. The default maximum size for this pool is 40, which is more than sufficient for almost all installations. &lt;br /&gt;
&lt;br /&gt;
If you have set a smaller value (e.g. 10), and have a very large amount of API or job activity, you can try increasing this value.&lt;br /&gt;
&lt;br /&gt;
Most frequently though, this issue arises because Obsidian is configured to use a JNDI data source with its own connection pool, and there are incompatibilities between the pools&#039; settings. As a rule, your JNDI database pool maximum size must be equal to or greater than the the maximum size of the Obsidian pool via property &amp;lt;code&amp;gt;com.carfey.obsidian.db.maxConnections&amp;lt;/code&amp;gt; as outlined in [[Advanced_Configuration#Database_Properties|Advanced Configuration]]. In addition, if there is an eviction process for idle connections, ensure than the value for the maximum idle age is 65 seconds or above to avoid warnings about unreleased connections. Alternatively, you can disable the Obsidian connection pool entirely by specifying &amp;lt;code&amp;gt;com.carfey.obsidian.db.disablePooling=true&amp;lt;/code&amp;gt; in your &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; file. This will cause Obsidian to never cache an opened idle connection.&lt;br /&gt;
&lt;br /&gt;
Here is a good base JNDI pool configuration for Tomcat 7.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;Resource name=&amp;quot;jdbc/obsidian&amp;quot; auth=&amp;quot;Container&amp;quot;&lt;br /&gt;
type=&amp;quot;javax.sql.DataSource&amp;quot;&lt;br /&gt;
driverClassName=&amp;quot;oracle.jdbc.OracleDriver&amp;quot;&lt;br /&gt;
url=&amp;quot;jdbc:oracle:thin:@localhost:1521/OBSIDIAN&amp;quot;&lt;br /&gt;
username=&amp;quot;OBSIDIAN&amp;quot;&lt;br /&gt;
password=&amp;quot;&amp;lt;password&amp;gt;&amp;quot;&lt;br /&gt;
maxActive=&amp;quot;50&amp;quot; maxIdle=&amp;quot;10&amp;quot;&lt;br /&gt;
maxWait=&amp;quot;2000&amp;quot; timeBetweenEvictionRunsMillis=&amp;quot;65000&amp;quot;&lt;br /&gt;
/&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= I&#039;m seeing deadlocks in the logs while running on MS SQL Server =&lt;br /&gt;
&lt;br /&gt;
MS SQL Server has different behaviour with the read committed isolation compared to other database platforms, which can result in periodic deadlocks in some installations.&lt;br /&gt;
&lt;br /&gt;
To change this and eliminate deadlocks, enable the [[Getting_Started#MS_SQL_Server_Snapshot_Isolation|read committed snapshot isolation]] on your database.&lt;br /&gt;
&lt;br /&gt;
= I&#039;m seeing an error in the logs about a time zone conflict. What&#039;s going on? =&lt;br /&gt;
&lt;br /&gt;
If you start multiple schedulers with different JVM time zones, Obsidian will abort startup to prevent inconsistent scheduling behaviour. The error will look something like &amp;quot;Cannot start scheduler in timezone [PST] since it conflicts with currently running node in timezone [EST]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Normally, if a scheduler is shut down, it can be started again with a new time zone setting. However, if a scheduler is not gracefully shut down, the stored time zone may persist. In this case, the following sample SQL can be used while all schedulers are stopped to clear out all time zone state:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Add table prefix as required.&lt;br /&gt;
DELETE FROM OPERATIONS_PARAMETER WHERE NAME LIKE &#039;%time%one%&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Spring_Integration&amp;diff=4160</id>
		<title>Spring Integration</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Spring_Integration&amp;diff=4160"/>
		<updated>2026-07-31T15:02:21Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Though Obsidian works great on its own, Obsidian also supports seamless integration with [http://projects.spring.io/spring-framework/ Spring]. This page outlines how to set up Spring integration and execute jobs which are configured as Spring components. It also documents a simple component which lets you easily embed a scheduler process within Spring.&lt;br /&gt;
&lt;br /&gt;
= Dependency Injection via Spring =&lt;br /&gt;
&lt;br /&gt;
Obsidian integrates with your Spring dependency injection container, and supports loading job implementations from the Spring context. Obsidian does not include any Spring libraries and will defer to the version and libraries you include with your application. It supports versions of Spring from 2.5 onward. &lt;br /&gt;
&lt;br /&gt;
Obsidian uses the &amp;lt;code&amp;gt;@Component&amp;lt;/code&amp;gt; and any Spring or custom extensions of those annotations (such as &amp;lt;code&amp;gt;@Service&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;@Repository&amp;lt;/code&amp;gt;) as a basis for knowing which job implementations should be retrieved from the Spring context. It is important to note that if a job class does not have one of these annotations, &#039;&#039;&#039;Obsidian will not use the container version&#039;&#039;&#039;, and will instantiate the job directly.&lt;br /&gt;
&lt;br /&gt;
Job classes which can be loaded from Spring can be either &amp;lt;code&amp;gt;SchedulableJob&amp;lt;/code&amp;gt; implementations or [[Implementing_Jobs#Annotation-Based_Jobs|annotated]] jobs. Even if you are not using Spring annotations to wire your classes, you will need to add the &amp;lt;code&amp;gt;@Component&amp;lt;/code&amp;gt; annotation (or a subtype) to your job classes to serve as a [http://en.wikipedia.org/wiki/Marker_interface_pattern marker] to Obsidian. See [[Implementing_Jobs|Implementing Jobs]] for details on how to write and configure executable jobs. &lt;br /&gt;
&lt;br /&gt;
Regardless of the method you use to configure your Spring components, Obsidian will load them directly from the container which means the actual singleton or prototype object obtained from Spring is executed by Obsidian. This makes creating jobs which use configured services very easy and convenient.&lt;br /&gt;
&lt;br /&gt;
Spring will need to be configured and started in all Obsidian scheduler runtimes, but not standalone Admin UI web apps. Each environment will require the job classes to be wired in Spring, scheduler nodes to permit retrieval from the context for execution, admin deployments to allow retrieval from the context for configuration validation.&lt;br /&gt;
&lt;br /&gt;
To allow Obsidian to locate your application&#039;s Spring context, you will need to ensure Obsidian&#039;s [https://docs.spring.io/spring/docs/3.2.18.RELEASE/api/org/springframework/context/ApplicationContextAware.html ApplicationContextAware] implementation is configured by Spring. If you are using Spring&#039;s classpath scanning, you can use the following approach to add an additional package to your configuration:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;context:component-scan base-package=&amp;quot;com.my.package.scan.base&#039;&#039;&#039;, com.carfey.ops.job.di&#039;&#039;&#039;&amp;quot;/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Another option is to extend our &amp;lt;code&amp;gt;com.carfey.ops.job.di.SpringContextAware&amp;lt;/code&amp;gt; class with an empty extension class in one of your currently scanned packages. When this object is configured by Spring, it will automatically initialize Obsidian&#039;s Spring integration.&lt;br /&gt;
&lt;br /&gt;
If you are not using scanning, simply ensure you wire in our &amp;lt;code&amp;gt;com.carfey.ops.job.di.SpringContextAware&amp;lt;/code&amp;gt; class via XML.&lt;br /&gt;
&lt;br /&gt;
If you have multiple instances of a given bean type in your Spring context, please ensure you use the &amp;lt;code&amp;gt;value=&amp;quot;&amp;quot;&amp;lt;/code&amp;gt; when you annotate your job with &amp;lt;code&amp;gt;@Component&amp;lt;/code&amp;gt; or its specializations as this is how Obsidian will uniquely identify the job within the Spring context.&lt;br /&gt;
&lt;br /&gt;
Implementations of &amp;lt;code&amp;gt;SchedulableJob&amp;lt;/code&amp;gt; found in your [https://docs.spring.io/spring/docs/3.2.18.RELEASE/api/org/springframework/context/ApplicationContext.html ApplicationContext] will automatically be available in the [[Admin_Jobs#Adding_.26_Editing_Jobs|Job Admin]] screen thus avoiding needing to configure distinct [[Implementing_Jobs#Classpath_Scanning|Classpath Scanning]].  If you&#039;re using at least Spring 3.0, your [[Implementing_Jobs#Annotation-Based_Jobs|annotated]] jobs will also be found in the Spring &amp;lt;code&amp;gt;ApplicationContext&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Grails ==&lt;br /&gt;
&lt;br /&gt;
Grails services can be used with the standard Spring Integration as outlined above, but they must have the appropriate &amp;lt;code&amp;gt;@Component&amp;lt;/code&amp;gt; annotation, or a subtype of that annotation.&lt;br /&gt;
&lt;br /&gt;
== Spring Boot ==&lt;br /&gt;
&lt;br /&gt;
See the [https://github.com/ajit-kulkarni/obsidian-demo Spring Boot sample project] showing how to start and stop the Obsidian scheduler automatically, courtesy of Ajit Kulkarni.&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
* Ensure Spring is initialized before Obsidian is started via either &amp;lt;code&amp;gt;com.carfey.ops.job.SchedulerStarter&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;com.carfey.ops.servlet.StartupShutdownListener&amp;lt;/code&amp;gt;. This ensures the Spring context is fully available when Obsidian starts running jobs.&lt;br /&gt;
** If using &amp;lt;code&amp;gt;com.carfey.ops.servlet.StartupShutdownListener&amp;lt;/code&amp;gt; within a &amp;lt;code&amp;gt;web.xml&amp;lt;/code&amp;gt; file, ensure the Spring context loader (e.g. &amp;lt;code&amp;gt;org.springframework.web.context.ContextLoaderListener&amp;lt;/code&amp;gt;) is called first.&lt;br /&gt;
** If using &amp;lt;code&amp;gt;com.carfey.ops.job.SchedulerStarter&amp;lt;/code&amp;gt; called via Spring initialization, ensure it is called at the end of the context initialization (e.g. &amp;lt;code&amp;gt;@PostConstruct&amp;lt;/code&amp;gt;, via an &amp;lt;code&amp;gt;InitializingBean&amp;lt;/code&amp;gt;, etc).&lt;br /&gt;
* If your job has internal state (i.e. members) which is used during job execution, Spring prototypes should be used to avoid concurrency issues.&lt;br /&gt;
* In many cases, the majority of job logic can be left in your existing Spring components.&lt;br /&gt;
* Use Obsidian to configure parameters and store results, but leave application-level configuration in Spring.&lt;br /&gt;
* Make sure every job you want to be loaded from the Spring context has a &amp;lt;code&amp;gt;@Component&amp;lt;/code&amp;gt; annotation, or a subtype of that annotation.&lt;br /&gt;
&lt;br /&gt;
== Sample Jobs ==&lt;br /&gt;
&lt;br /&gt;
As a prototype &amp;lt;code&amp;gt;@Component&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@Component&lt;br /&gt;
@Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE)&lt;br /&gt;
@Configuration(knownParameters={&lt;br /&gt;
		@Parameter(name=&amp;quot;incrementCounter&amp;quot;, type=Type.BOOLEAN, defaultValue=&amp;quot;true&amp;quot;, required=true)&lt;br /&gt;
})&lt;br /&gt;
@Description(&amp;quot;This job is a Spring Bean that invokes an Autowired Counter Service.&amp;quot;)&lt;br /&gt;
public class SpringComponentJob implements SchedulableJob {&lt;br /&gt;
&lt;br /&gt;
    @Autowired private CounterService mCounterService; &lt;br /&gt;
    private MyState mState;&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public void execute(Context context) throws Exception {&lt;br /&gt;
        ...snip... &lt;br /&gt;
        feel free to call methods that manipulate state since this is a prototype&lt;br /&gt;
        ...snip... &lt;br /&gt;
        String prefix = SpringComponentJob.class.getName();&lt;br /&gt;
        if (context.getConfig().getBoolean(&amp;quot;incrementCounter&amp;quot;)) {&lt;br /&gt;
	    context.saveJobResult(prefix + &amp;quot;.incrementCounter&amp;quot;, mCounterService.incrementCounter());&lt;br /&gt;
	} else {&lt;br /&gt;
	    context.saveJobResult(prefix + &amp;quot;.currentCounter&amp;quot;, mCounterService.currentValue());&lt;br /&gt;
	}&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As a singleton &amp;lt;code&amp;gt;@Service&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@Service&lt;br /&gt;
@Configuration(knownParameters={&lt;br /&gt;
		@Parameter(name=&amp;quot;incrementCounter&amp;quot;, type=Type.BOOLEAN, defaultValue=&amp;quot;true&amp;quot;, required=true)&lt;br /&gt;
})&lt;br /&gt;
@Description(&amp;quot;This job is a Spring Service that makes various service calls.&amp;quot;)&lt;br /&gt;
public class SpringServiceJob implements SchedulableJob {&lt;br /&gt;
&lt;br /&gt;
    @Autowired private PricingService mPricingService; &lt;br /&gt;
    @Autowired private AuditingService mAuditingService; &lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public void execute(Context context) throws Exception {&lt;br /&gt;
        ...snip... &lt;br /&gt;
        feel free to call other services or local service methods&lt;br /&gt;
        ...snip... &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Wiring an Embedded Obsidian Scheduler =&lt;br /&gt;
&lt;br /&gt;
Obsidian can run scheduler nodes that do not include the full web application deployment. This is often used in conjunction with a separate standalone admin web application.&lt;br /&gt;
&lt;br /&gt;
Obsidian comes bundled with a simple Spring component to automatically start and stop an embedded scheduler. &amp;lt;code&amp;gt;com.carfey.ops.job.di.SpringSchedulerStarter&amp;lt;/code&amp;gt; can be wired into your Spring application via XML:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;!-- This should generally be as late in context startup as possible, using depends-on, etc. as required --&amp;gt;&lt;br /&gt;
 &amp;lt;bean id=&amp;quot;obsidianStarter&amp;quot; class=&amp;quot;com.carfey.ops.job.di.SpringSchedulerStarter&amp;quot; /&amp;gt; &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This wired bean will automatically start and stop the scheduler gracefully when the context initializes and shuts down.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; For safety reasons, this class does not possess a &amp;lt;code&amp;gt;@Component&amp;lt;/code&amp;gt; annotation and cannot be auto-wired via Spring classpath scanning.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Scripting_Jobs&amp;diff=4159</id>
		<title>Scripting Jobs</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Scripting_Jobs&amp;diff=4159"/>
		<updated>2026-07-31T15:02:12Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian provides support for execution of scripted jobs in various languages. None of these require any Java code to be written. The script to execute is set via a &#039;&#039;script&#039;&#039; job parameter. Jobs are executed under the context of the process user running Obsidian.&lt;br /&gt;
&lt;br /&gt;
The following job classes can be configured with executable scripts in their target languages:&lt;br /&gt;
&lt;br /&gt;
* com.carfey.ops.job.script.JavaScriptJob running [https://github.com/mozilla/rhino Rhino] or [https://wiki.openjdk.java.net/display/Nashorn/Main Nashorn] depending on your Java version.&lt;br /&gt;
* com.carfey.ops.job.script.PythonJob running [https://www.jython.org/ Jython]&lt;br /&gt;
* com.carfey.ops.job.script.GroovyJob [https://groovy-lang.org/ Groovy]&lt;br /&gt;
* com.carfey.ops.job.script.BeanShellJob executing a [https://beanshell.github.io/ BeanShell] script.&lt;br /&gt;
* com.carfey.ops.job.script.RubyJob executing a [https://www.jruby.org/ JRuby] script. &#039;&#039;Supported as of Obsidian 4.10.0&#039;&#039;&lt;br /&gt;
* com.carfey.ops.job.script.ReflectiveJob is a convenience GroovyJob that does not require a script, using defined parameters to construct an object and invoke method(s), both supporting argument(s)&lt;br /&gt;
* &amp;lt;strike&amp;gt;com.carfey.ops.job.script.[[#SpringBeanJob|SpringBeanJob]]&amp;lt;/strike&amp;gt; is a convenience GroovyJob that does not require a script, using defined parameters to obtain the bean and invoke the method(s). Deprecated. Use [[Implementing_Jobs#Dependency_Injection_via_Spring|Dependency Injection]] instead.&lt;br /&gt;
&lt;br /&gt;
= Parameters =&lt;br /&gt;
Any parameters configured for the job will be set as global variables within the script. If a single value is configured for a parameter, it will be set as its direct type. If multiple values are set for a parameter, it will be passed into the script as a list of values.&lt;br /&gt;
&lt;br /&gt;
== Loading Scripts from the File System ==&lt;br /&gt;
&lt;br /&gt;
All script file jobs can use a script file path instead of configuring the script directly in Obsidian. When the job runs, Obsidian will load the contents of the file and execute the script in the same way that jobs configured using the &amp;lt;code&amp;gt;script&amp;lt;/code&amp;gt; parameter are run.&lt;br /&gt;
&lt;br /&gt;
To use this feature, when configuring the job, omit the &amp;lt;code&amp;gt;script&amp;lt;/code&amp;gt; parameter and instead supply a file path under &amp;lt;code&amp;gt;Script File Path&amp;lt;/code&amp;gt;. It is recommended that an absolute path is used, and the Obsidian process will need read access to the file for it to run successfully.&lt;br /&gt;
&lt;br /&gt;
= Job Context =&lt;br /&gt;
The job context variable is accessed through the global variable named &amp;quot;jobContext&amp;quot;. This can be used to save output parameters, etc. See [[Implementing Jobs#Job Results|Job Results]] and [[Implementing Jobs#Parameterization|Parameterization]].&lt;br /&gt;
&lt;br /&gt;
= Sample Jobs =&lt;br /&gt;
&lt;br /&gt;
Groovy:&lt;br /&gt;
&lt;br /&gt;
[[File:GroovyConfig.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Python:&lt;br /&gt;
&lt;br /&gt;
[[File:PythonConfig.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Javascript:&lt;br /&gt;
&lt;br /&gt;
[[File:JavascriptConfig.png]]&lt;br /&gt;
&lt;br /&gt;
= &amp;lt;div id=&amp;quot;SpringBeanJob&amp;quot;&amp;gt;SpringBeanJob&amp;lt;/div&amp;gt; =&lt;br /&gt;
&#039;&#039;&#039;Deprecated.&#039;&#039;&#039; Use [[Implementing_Jobs#Dependency_Injection_via_Spring|Dependency Injection]] instead.&lt;br /&gt;
A SpringBeanJob is simply a convenience GroovyJob with a predefined script that uses some specified parameters to find a Spring Bean and invoke specified method(s).  Specify any optional class imports (class names only), the single line of code to retrieve the Spring Bean reference and the method name(s) to invoke.&lt;br /&gt;
&lt;br /&gt;
[[File:SpringBeanJobSample.png|300px]]&lt;br /&gt;
&lt;br /&gt;
= Running Ruby from Obsidian =&lt;br /&gt;
Obsidian can be configured to run Ruby via JRuby. Jython support and JRuby conflict with each other due to conflicting dependencies. As of Obsidian 4.10.0, Obsidian supports JRuby out of the box, but will only allow you to select one of the two - JRuby or Jython. Prior to Obsidian 4.10.0, if you are willing to drop Jython(Python) support in Obsidian, you may follow these instructions to enable JRuby support.&lt;br /&gt;
&lt;br /&gt;
# Download [http://jruby.org/ JRuby] JAR and replace the Jython JAR (jython-standalone-&#039;&#039;n&#039;&#039;.&#039;&#039;n&#039;&#039;.&#039;&#039;n&#039;&#039;.jar) with the JRuby JAR.&lt;br /&gt;
# Add the RubyJob source below. Either incorporate into your existing job building process or compile and statically add to your Obsidian deployment.&lt;br /&gt;
# Note the sample configuration below.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
The MIT License (MIT)&lt;br /&gt;
&lt;br /&gt;
Copyright ©2019 Carfey Software Corporation. All rights reserved.&lt;br /&gt;
&lt;br /&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy&lt;br /&gt;
of this software and associated documentation files (the &amp;quot;Software&amp;quot;), to deal&lt;br /&gt;
in the Software without restriction, including without limitation the rights&lt;br /&gt;
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell&lt;br /&gt;
copies of the Software, and to permit persons to whom the Software is&lt;br /&gt;
furnished to do so, subject to the following conditions:&lt;br /&gt;
&lt;br /&gt;
The above copyright notice and this permission notice shall be included in&lt;br /&gt;
all copies or substantial portions of the Software.&lt;br /&gt;
&lt;br /&gt;
THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR&lt;br /&gt;
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,&lt;br /&gt;
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE&lt;br /&gt;
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER&lt;br /&gt;
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,&lt;br /&gt;
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN&lt;br /&gt;
THE SOFTWARE.&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
package com.carfey.ops.job.script;&lt;br /&gt;
&lt;br /&gt;
import com.carfey.ops.job.param.Description;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * &amp;lt;p&amp;gt;&lt;br /&gt;
 * &amp;lt;b&amp;gt;Usage: &amp;lt;/b&amp;gt;&lt;br /&gt;
 * See {@link ScriptEngineJob}. &lt;br /&gt;
 * This is a script job using the ruby engine,&lt;br /&gt;
 * which by default is the &amp;lt;a href=&amp;quot;http://jruby.org/&amp;quot;&amp;gt;JRuby&amp;lt;/a&amp;gt; implementation.&lt;br /&gt;
 * &lt;br /&gt;
 * &lt;br /&gt;
 * @see &amp;lt;a href=&amp;quot;https://wiki.obsidianscheduler.com/docs/File:Obsidian.3.6.0.RubySampleJob.PNG&amp;quot;&amp;gt;Sample Configuration&amp;lt;/a&amp;gt; &lt;br /&gt;
 */&lt;br /&gt;
@Description(&amp;quot;This job will execute the supplied Ruby script, with custom parameters inserted into the script variable context. &amp;quot; +&lt;br /&gt;
        &amp;quot;Either an inline script or the full script path may be supplied.&amp;quot;)&lt;br /&gt;
public class RubyJob extends ScriptEngineJob {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    protected String getEngineName() {&lt;br /&gt;
        return &amp;quot;jruby&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:RubyConfig.png]]&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Release_Notes_-_Older_Releases&amp;diff=4158</id>
		<title>Release Notes - Older Releases</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Release_Notes_-_Older_Releases&amp;diff=4158"/>
		<updated>2026-07-31T14:49:59Z</updated>

		<summary type="html">&lt;p&gt;Craig: De-link the deleted Legacy API page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;toclimit-2&amp;quot;&amp;gt;__TOC__&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.10.6 == &lt;br /&gt;
Released April 2021.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
* Correct issue where PostgreSQL database connection validation exceptions are masked.&lt;br /&gt;
* Fix UI styling issues in Firefox browser.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.10.5 == &lt;br /&gt;
Released October 2020. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* New &#039;&#039;LW&#039;&#039; instruction for day-of-month field in [[Cron#Special_Character_Usage|Cron]] to indicate last weekday of month.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
* Fix for &#039;&#039;W&#039;&#039; day-of-month field in [[Cron#Special_Character_Usage|Cron]] skipping months where ordinal is last ordinal of the month and falls on the last Sunday of the month.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.10.4 == &lt;br /&gt;
Released July 2020. &lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
* Fix UTF-8 handling in [[REST_API|REST API]] calls.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.10.3 == &lt;br /&gt;
Released March 2020. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Introduce better handling for edge case where a long running job that loses connectivity to the Obsidian database gets marked as Died while it is still running and then eventually reconnects to the database and its heartbeat updates resume. Heartbeat updates will now be prevented and new special events are triggered when this case is detected and when the job finally terminates.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes ===&lt;br /&gt;
* Fix JobIds parameter in [[Embedded_API#List_Job_Dashboard_.28Latest_Scheduled_Runtime_by_Job.29|Embedded]] API for Job Dashboard (Last Occurrence of Jobs)&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.10.2 == &lt;br /&gt;
Released October 2019. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* [[REST_Endpoints#GET_a_list_of_the_latest_scheduled_runtime_by_job_.28supports_multiple_jobs.29|REST]] and [[Embedded_API#List_Job_Dashboard_.28Latest_Scheduled_Runtime_by_Job.29|Embedded]] API for Job Dashboard (Last Occurrence of Jobs)&lt;br /&gt;
* Timezone now included when dates are in event messages.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.10.1 == &lt;br /&gt;
Released June 2019. &lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* [[Recovery_%26_Failover#Startup_.2F_Shutdown_Mode|Startup/Shutdown]] configuration of jobs not being applied in UI.&lt;br /&gt;
* [[Admin_Scheduler_Settings|Scheduler setting]] jobShutdownWaitTimeSeconds in Job category could not be set to -1 in the UI.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.10.0 == &lt;br /&gt;
Released April 2019. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Support for automated [[Recovery_%26_Failover#Startup_.2F_Shutdown_Mode|Startup/Shutdown]] invocation of jobs.&lt;br /&gt;
* [[Implementing_Jobs#Parameterization|Parameters]] now have optional support for validation of list values to be unique.&lt;br /&gt;
* Formalized support for [[Scripting_Jobs|Ruby Script jobs]] via JRuby.&lt;br /&gt;
* Enhanced [[Installation_Guide#Selecting_Script_Libraries|installer]] to allow selectivity of desired script libraries/jobs. &#039;&#039;&#039;Breaking change to automated installer files.&#039;&#039;&#039; &amp;lt;ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using automated installer files from previous releases, you will need to add a new section of xml as of Obsidian 4.10.0 to handle a new UserInputPanel. Immediately after the UserInputPanel.15 closing brace, add the following:&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;com.izforge.izpack.panels.UserInputPanel id=&amp;quot;UserInputPanel.16&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;userInput&amp;gt;&lt;br /&gt;
&amp;lt;entry key=&amp;quot;script.beanshell&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;entry key=&amp;quot;script.jruby&amp;quot; value=&amp;quot;false&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;entry key=&amp;quot;script.groovy&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;entry key=&amp;quot;script.jython&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;&lt;br /&gt;
&amp;lt;/userInput&amp;gt;&lt;br /&gt;
&amp;lt;/com.izforge.izpack.panels.UserInputPanel&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can change the parameter values as desired. The above reflects the options for all versions prior to Obsidian 4.10.0.&lt;br /&gt;
&amp;lt;/ref&amp;gt;&lt;br /&gt;
* Formalized support for [[Getting_Started#Supported_Platforms|MS SQL 2016 &amp;amp; 2017]]&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.9.1 == &lt;br /&gt;
Released March 2019. &lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* ClasspathJobScanner fails on job classes with no [[Implementing_Jobs#SchedulableJob_Interface|@Description]] annotation&lt;br /&gt;
* In some cases, [[Built-in_Jobs#Job_History_Cleanup_Job|Job History Cleanup Job]] was not deleting certain history and related records.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.9.0 == &lt;br /&gt;
Released February 2019. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* [[Implementing_Jobs#SchedulableJob_Interface|Description]]/[[Implementing_Jobs#Parameterization|Parameter]] annotation support optional URLs arrays&lt;br /&gt;
* New built-in maintenance job [[Built-in_Jobs#Notification_Cleanup_Job|Delete Notifications Job]]&lt;br /&gt;
* New subscribable event for first occurrence of a host not running a job due to not being one of the fixed hosts.&lt;br /&gt;
* [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/Context.html Context] class expanded to include some additional helpful attributes.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* [[Admin_Jobs#Nickname.2C_Class_and_Folder|Folder]] Selection in UI fixed to allow intermediary folders selections&lt;br /&gt;
* [[Event_Hooks#Slack_Notifier|SlackEventHook]] fixed to include ERROR level&lt;br /&gt;
* Logout from My User page fixed to redirect to Login page&lt;br /&gt;
* Fixed an issue with PostgreSQL DDL that could report wrong exception on execution failures.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.8.0 == &lt;br /&gt;
Released November 2018. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Improve performance of job activity view when retrieving large job errors.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Corrected an issue where in some situations job conflict priority calculation was incorrect.&lt;br /&gt;
* Missed and Overlapped job events are now subscribable.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.7.1 == &lt;br /&gt;
Released October 2018. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* New LDAP [[Advanced_Configuration#Authentication_Properties|Configuration Parameter]] to support additional group membership attributes.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Correct Non-English Locale issues in database DDL/DML.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.7.0 == &lt;br /&gt;
Released September 2018. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Optimized [[Built-in_Jobs#Job_History_Cleanup_Job|JobHistoryCleanupJob]] for better performance and reduced locking&lt;br /&gt;
* New [[REST_Endpoints#DELETE_a_future_scheduled_runtime|REST]] and [[Embedded_API#Delete_a_Future_Scheduled_Runtime_for_a_Job|Embedded]] API function for targeted deletion of a single future one time submission including its parameters.&lt;br /&gt;
* Modified job_state index to improve job listing performance in cases where there are a large of future-dated [[Job_Features#Resubmission_.26_Ad_Hoc_Runs|ad-hoc submissions]].&lt;br /&gt;
* PostgreSQL 10 Support&lt;br /&gt;
* Oracle 12c support&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Add missing job_history index on PostgreSQL installations&lt;br /&gt;
* Corrected issue where Get Schedules [[Embedded_API#List_a_Job.27s_Schedules|Embedded]] and [[REST_Endpoints#GET_a_list_of_an_existing_job.27s_schedules|REST]] APIs were setting all parameters to the ordinal of the last parameter.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.6.2 == &lt;br /&gt;
Released August 2018. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Reduce locking in certain cases of job delete.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Corrected NPE when performing recovery on a job that has transitioned from Disabled to Enabled state.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.6.1 == &lt;br /&gt;
Released July 2018. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Performance improvements related to Job Result loading.&lt;br /&gt;
* Added informational logging for cases where licence key configuration will not applied.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Corrected issue where stale admin_only hosts were not cleaned from database.&lt;br /&gt;
* Corrected issue where licence release failures were never logged due to shutdown state.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.6.0 == &lt;br /&gt;
Released June 2018. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* [[Advanced_Configuration#Miscellaneous_Properties|Support]] for starting a scheduler node in paused state.&lt;br /&gt;
* Several performance improvements for [[Admin_Job_Activity|Job Activity]] view.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Removed class reference that was causing failures running Obsidian against Java 10.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.5.1 == &lt;br /&gt;
Released April 2018. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Upgrade of [[Advanced_Configuration#Dependent_Libraries|bundled Groovy library]] which addresses known Groovy memory leaks.&lt;br /&gt;
* [[Getting_Started#Standalone_Scheduler|Standalone mode]] now attempts graceful shut down when SIGTERM is issued.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* [[Authenticator#Customizing_our_LDAP_Authenticator|LDAPAuthenticator]] no longer fails when combining delimited base.dn with search user.&lt;br /&gt;
* Defensively protect against possible stack overflow in [[Spring_Integration#Dependency_Injection_via_Spring|SpringContextAware]] for cyclical annotation hierarchies.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.5.0 == &lt;br /&gt;
Released March 2018. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* [[Event_Hooks|Custom Event Hooks]] allow for custom triggers on Obsidian events for things such as instant messaging, customized logging, etc.&lt;br /&gt;
* [[Event_Hooks#Slack_Notifier|Slack Notifier]] implemented as an [[Event_Hooks|Event Hook]]&lt;br /&gt;
* [[Implementing_Jobs#Async_Jobs|Async job]] support&lt;br /&gt;
* [[Implementing_Jobs#Dynamic_List_Providers|Dynamic List Provider]] support&lt;br /&gt;
* LDAP [[Authenticator|Authenticator]] to support delimited value [[Advanced_Configuration#Authentication_Properties|configurations]] for dn.base and Obsidian role/group mapping.&lt;br /&gt;
* Directory/File classes now provide folder/file in question throwing underlying filesystem exceptions.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* [[Recovery_%26_Failover#Configurable_Job_Recovery|Last recovery]] cron patterns no longer fail crossing some boundaries&lt;br /&gt;
* Not all job attributes were being applied in Obsidian [[Initializing_and_Restoring#Startup_Initialization|initialization file]]. Pickup buffer minutes wasn&#039;t being used in both create and update cases, job folder wasn&#039;t being used in update cases.&lt;br /&gt;
* Correct invalid Oracle index name&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.4.1 == &lt;br /&gt;
Released January 2018. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Job Queuer, Spawn Locking, Last Recovery performance improvements, includes a new database index.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Admin UI restores previous search filter settings. In cases where the last job with a given Job Folder was deleted, or for screens that filter by Job Nickname, the filter was still applied but not visible resulting in no results returned. Now, any invalid options are automatically removed.&lt;br /&gt;
* [[Recovery_%26_Failover#Configurable_Job_Recovery|Last Recovery]] in rare cases would recover too far back.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.4.0 == &lt;br /&gt;
Released October 2017. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Fixed Hosts now support [[Admin_Jobs#Advanced_Options | Blacklists]]&lt;br /&gt;
* Job Results now support [[Implementing_Jobs#Customizing_Job_Result_Serialization_and_Deserialization | Custom Serialization/Deserialization]] and default Gson JSON for complex objects.&lt;br /&gt;
* [[Advanced_Configuration#Properties_File|Configuration]] values may be specified as System Properties/Environment Variables.&lt;br /&gt;
* Obsidian upgrades now support [[Getting_Started#Disabling_DDL_Updates|data-only upgrades]] (structural changes are pre-applied offline).&lt;br /&gt;
* Job classes may now be partially introduced into the cluster and subsequently scheduled/executed without being attempted on missing hosts avoiding failures (via JobSpawner parameter &#039;&#039;skipSpawnAttemptsForUnavailableJobs&#039;&#039;).&lt;br /&gt;
* [[Embedded API]] and [[REST API]]s now return basic audit values (created/updated user/time)&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Obsidian upgrades spanning multiple structural and data changes to a given table no longer fail.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.3.0 == &lt;br /&gt;
Released June 2017. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* [[REST_Endpoints#GET_licence_details_on_an_existing_scheduling_host |REST Endpoint]] and [[Embedded_API#Get_Licence_Health_by_Host|Embedded API]] for Licensing Health Check&lt;br /&gt;
* Support Markdown and customizable [[Description_%26_Parameter_Formatting|Formatting]] in Job description and Parameter description annotations&lt;br /&gt;
* Support timed wait as the new default on FOR UPDATE statements in Oracle - [[Advanced_Configuration#Database_Properties | Advanced Configuration - Database Properties]]. Old or customized behaviour possible via configuration properties.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fix issue in [[Built-in_Jobs#Disabled_Job_Cleanup_Job|DisabledJobCleanupJob]] that fails to delete disabled jobs with chained and/or resubmitted records.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.2.1 == &lt;br /&gt;
Released May 2017. &lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* [[Cron#Recurrence|Recurrence]] schedules in server recovery will fail if last runtime doesn&#039;t exist.&lt;br /&gt;
* A job that is configured against a job class that has since been removed from the cluster can now be [[Admin_Jobs|loaded]]/[[Admin_Jobs#Deleting|deleted]] from the UI.&lt;br /&gt;
* [[Admin_Jobs#Deleting|Delete Job]] - If the delete operation fails for any reason and the operator leaves the modal and returns, the error from the previous operation was still visible.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.2.0 == &lt;br /&gt;
Released March 2017. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Time Zone support in [[Cron|Cron]] patterns. Allows a cluster running in a single time zone to run jobs designated in different timezones.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* LAST [[Recovery_%26_Failover#Configurable_Job_Recovery|recovery mode]] wouldn&#039;t fire latest job on cluster recovery if the job had never fired or if the last runtime had been deleted by the maintenance job.&lt;br /&gt;
* ALL [[Recovery_%26_Failover#Configurable_Job_Recovery|recovery mode]] would fire first missed job, but the remaining ones wouldn&#039;t run and would be set as OVERLAPPED.&lt;br /&gt;
* When creating a new schedule that overlaps one or more previously configured future runtimes with parameters, parameters are now deleted from the database.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.1.0 == &lt;br /&gt;
Released February 2017. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* [[Admin_Jobs#Nickname.2C_Class_and_Folder|Folder]]/tag support for jobs including a [[REST_Endpoints#GET_a_list_of_job_folders|REST]] endpoint and [[Embedded_API#List_Job_Folders|Embedded]] method.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* UI issue in Job Execution Subscriptions and Templates - selected jobs now displaying correctly after save/refresh.&lt;br /&gt;
* [[Initializing_and_Restoring#Transfer_or_Restore_a_Configuration|UpdateConfigurationAction]] no longer fails in non-native Authentication mode if an empty Users element is provided.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.0.2 == &lt;br /&gt;
Released October 2016. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* [[Implementing_Jobs#Parameterization|Job Parameter]]s now support an optional &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; to be displayed in UI Help.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fixed an issue where [[Cron#Special_Character_Usage|Cron special character]] &amp;lt;code&amp;gt;L&amp;lt;/code&amp;gt; would not work if specified as the only value in a field.&lt;br /&gt;
* Fixed 3.8.0 MySQL upgrade script.&lt;br /&gt;
* Fixed an issue where Obsidian continued to attempt regular licence verification after a valid site licence had been configured.&lt;br /&gt;
* Fixed an issue where table prefixes weren&#039;t used in all lookups.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.0.1 == &lt;br /&gt;
Released August 2016. &lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fixed bug where a chained job that became conflicted would not evaluate chain conditions once it was no longer conflicted.&lt;br /&gt;
* Fixed catalina.base bug and dos line endings in jetty.sh script in bundled jetty server.&lt;br /&gt;
* Fixed job parameter edit box resizing bug.&lt;br /&gt;
&lt;br /&gt;
== Obsidian 4.0.0 == &lt;br /&gt;
Released July 2016. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* All New User Interface&lt;br /&gt;
* Obsidian is now built using [[Getting_Started#Supported_Platforms|Java 7]]. No longer supports Java 6.&lt;br /&gt;
* [[Getting_Started#Really_Quick_Start_With_H2_and_Jetty|Quick Start]] now uses Jetty.&lt;br /&gt;
* Many [[Advanced_Configuration#Dependent_Libraries|updated libraries]] including some that had been updated to Java 7. &lt;br /&gt;
** &#039;&#039;Breaking Change&#039;&#039; New H2 library. If you&#039;re using the H2 database, you will want to look into [[Initializing_and_Restoring|exporting and restoring]] your Obsidian environment&#039;s configuration.&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* [[REST_Endpoints#GET_a_system_restore_configuration|GetConfiguration]] filtering no longer fails when Templates/Subscriptions are job targeted.&lt;br /&gt;
&lt;br /&gt;
== All Other Releases ==&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.8.0 === &lt;br /&gt;
Released June 2016. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* [[Job_Features#Resubmission_.26_Ad_Hoc_Runs|Ad Hoc]] runs now may be submitted multiple times per minute. [[Chaining|Chains]] may result in target jobs multiple times per minute. Existing behaviour of [[Admin_Jobs#Execution_.26_Pickup|Chain All]] job configuration remains unchanged.&lt;br /&gt;
* [[Built-in_Jobs#Script_Job|Script Job]] now supports best-effort interruption.&lt;br /&gt;
* APIs for GET System Restore Configuration support filtering for both [[REST_Endpoints#GET_a_system_restore_configuration|REST]] and [[Embedded_API#Get_System_Restore_Configuration|Embedded]].&lt;br /&gt;
* [[Implementing_Jobs#Parameterization|Context]] for job execution now supports retrieving the chain source job config, when applicable. See also the Javadoc for [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/Context.html#getSourceConfig() Context].&lt;br /&gt;
* [[Getting_Started#Setting_Host_Names|Host names]] maximum length increased to 255 characters from 50.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fixed an issue where [[Admin_Jobs#Execution_.26_Pickup|auto-interrupts]] were not firing.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.7.1 === &lt;br /&gt;
Released April 2016. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements ===&lt;br /&gt;
* Job APIs windows now with improved locking/blocking on schedule modifications.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* [[REST_Endpoints#GET_a_system_restore_configuration|GET SystemRestoreConfiguration]] no longer fails when using non-native authentication.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.7.0 ===&lt;br /&gt;
Released March 2016. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* New maintenance job [[Built-in_Jobs#Disabled_Job_Cleanup_Job|Disabled Job Cleanup Job]].&lt;br /&gt;
* Support for parameters that are [[Implementing_Jobs#Parameterization|required at runtime]].&lt;br /&gt;
* Filter [[REST_Endpoints#GET_a_list_of_an_existing_job.27s_schedules|job schedules endpoint]] by start date and/or end date.&lt;br /&gt;
* Return [[REST_Endpoints#GET_details_of_an_existing_job|runtime parameters]] for schedule-based one-time runs.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* [[Embedded_API#Get_System_Restore_Configuration|SystemRestoreConfiguration]] export was only including the last schedule of a job.&lt;br /&gt;
* Admin-only hosts fail on startup if not in consistent timezone as cluster.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.6.1 === &lt;br /&gt;
Released February 2016. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* Enhance JobQueuer to reduce execution time when dealing with large numbers of chain-only jobs.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fix issue where job that is scheduled and chained is scheduled late after being chained.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.6.0 === &lt;br /&gt;
Released January 2016. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[Job_Forking|Forked Jobs]] offer jjdchinga tu mad[[Advanced_Configuration#Properties_File|Classpath com.carfey.properties]] no longer required. Applicable when using overrides.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* AutoInterrupt not being set on jobs from [[Initializing_and_Restoring#Jobs|System Restore]].&lt;br /&gt;
* RunningHostManager can, in rare cases, evaluate job metadata as a HostInformation entry.&lt;br /&gt;
* Schedule calculation on outage looks back one minute too far when considering pickup buffer.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.5.2 === &lt;br /&gt;
Released December 2015.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes === &lt;br /&gt;
* With native authentication, at least one Admin User must still exist when making [[Admin_User_Management#Changing_Users|Role Changes]].&lt;br /&gt;
* Cleanup of some internal host management functionality.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.5.1 === &lt;br /&gt;
Released November 2015.&lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements === &lt;br /&gt;
* Obsidian nodes that are set to different timezones but have the same rules (functionally equivalent such as &#039;&#039;America/Los_Angeles&#039;&#039; and &#039;&#039;PST&#039;&#039;) may now be run together.&lt;br /&gt;
* [[Authenticator|LDAPAuthenticator]] now supports [[Advanced_Configuration#Authentication_Properties|non-dn user attribute logins]].&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* [[Admin_User_Management#Viewing_Rights_.26_Changing_Your_Password|My User]] screen would fail when using LDAPAuthenticator. Now shows authorized roles.&lt;br /&gt;
* Stale host and timezone records are predeleted on startup.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.5.0 === &lt;br /&gt;
Released November 2015. &lt;br /&gt;
&lt;br /&gt;
=== Features / Enhancements === &lt;br /&gt;
* Support for scanning and locating CGLIB-enhanced classes in [[Spring_Integration|Spring]] and Grails.&lt;br /&gt;
* Improved [[Admin_Job_Run_Time_Preview|runtime preview]] listing performance.&lt;br /&gt;
* Automatic logging of Obsidian version number no longer requires special configuration.&lt;br /&gt;
* [[Authenticator]] implementations including LDAP now support checking user validity when the &amp;quot;remember me&amp;quot; option is used to authenticate.&lt;br /&gt;
* Runtime listing [[REST_Endpoints#GET_a_list_of_scheduled_runtimes_.28supports_multiple_jobs.29|REST endpoint]] and [[Embedded_API#List_Scheduled_Runtimes|Embedded API]] now support specifying a sort order, maximum quantity and parameter filtering, and now guarantee ordering by scheduled time.&lt;br /&gt;
&lt;br /&gt;
=== Bug Fixes === &lt;br /&gt;
* Capped [[Admin_Job_Run_Time_Preview|runtime preview]] results now respect ascending ordering.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.4.1 === &lt;br /&gt;
Released September 2015. &lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Remove undesired field jobHistoryRunningId from runtime listing REST endpoint.&lt;br /&gt;
* Fixed standalone scripts that would not shutdown instance when hostname could not be determined.&lt;br /&gt;
* Shell scripts should always exit with non-zero code on error.&lt;br /&gt;
* Improve forked job shell script compatibility.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.4.0 === &lt;br /&gt;
Released September 2015. &lt;br /&gt;
&lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[Cron#Recurrence_Start_DateTime|Recurrence]] supports a start datetime.&lt;br /&gt;
* [[Implementing_Jobs#Interruptable_Jobs|Interruptable Jobs]] support [[Job_Features#Expected_Length_Handling_.28job_execution_duration.29|Auto Interruption]] when maximum expected run length is exceeded.&lt;br /&gt;
* [[Implementing_Jobs#Global_Parameters|Global Parameters]] support [[Implementing_Jobs#Global_Substitution_Mode|Global Substitution Mode]].&lt;br /&gt;
* Added &#039;&#039;jobClass&#039;&#039; parameter to job search in REST and Embedded APIs.&lt;br /&gt;
* Performance improvements in cron and recurrence calculations.&lt;br /&gt;
* Add database index for recovery performance.&lt;br /&gt;
* Support for [[Advanced_Configuration#Database_Properties|disabling internal connection pool]].&lt;br /&gt;
* Support [[Advanced_Configuration#Miscellaneous_Properties|base HREF overrides]] for load balancers, etc.&lt;br /&gt;
* JobHistoryCleanupJob always cleans up expired schedules according to &amp;lt;code&amp;gt;maxAgeDays&amp;lt;/code&amp;gt; parameter.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Default sequence_manager pool to 10% of connections with min of 2.&lt;br /&gt;
* Fix issue where parameter order is not maintained consistently on PostgreSQL&lt;br /&gt;
* Fix issue where JobHistoryCleanupJob misses cleaning up jobs that are in MISSED state.&lt;br /&gt;
* JobHistoryCleanupJob leaves latest runtime record to ensure Recurrence schedules fire at correct time.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.3.3 === &lt;br /&gt;
Released August 2015. &lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Chain Skipped and Conflict Missed job runtime enum values corrected to use underscores in REST/Embedded API when listing runtimes.&lt;br /&gt;
* In rare cases, outages or queuing delays could prevent first job history from ever being queued.&lt;br /&gt;
* Job initial schedule end time not editable via text entry when cloned.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.3.2 === &lt;br /&gt;
Released June 2015. &lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Restore missing fields on Runtime and RuntimeError classes for REST/Embedded API.&lt;br /&gt;
* Defensive handling of missing JOB_STATE records in chaining.&lt;br /&gt;
* Job class field in job edit screen loses content if enter is pressed without match.&lt;br /&gt;
* Obsidian version added to dispatcher logs.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.3.1 === &lt;br /&gt;
Released June 2015. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* Case-sensitive collation support for SQL Server.&lt;br /&gt;
* Unicode support on SQL Server (please note that existing schemas will not be upgraded automatically).&lt;br /&gt;
* Unicode fixes for all platforms.&lt;br /&gt;
* Whitespace characters permitted in between delimited schedules.&lt;br /&gt;
* Improved logging on warning and error conditions.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* JobSearch not working on Oracle since 3.3.0.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.3.0 === &lt;br /&gt;
Released May 2015. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[Implementing_Jobs#List_Parameterization|Job parameterization]] support for picklists of valid values.&lt;br /&gt;
* [[Implementing_Jobs#Dynamic_File_Lists|Server-side file listing]] support.&lt;br /&gt;
* Support for multiple [[Cron#Basic_Format|Cron]] schedule patterns.&lt;br /&gt;
* [[Admin_Job_Chains#Chain_Tree_View|Tree visualization of chains]].&lt;br /&gt;
* [[Admin_Job_Chains#Conditional_Chaining_Configuration|Conditional chains]] now support text ordinal comparisons.&lt;br /&gt;
* Added optional [[Admin_Job_Chains#Adding_or_Updating_a_Job_Chain|chain description]].&lt;br /&gt;
* Spawning and queuing performance improvements.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Expired job schedules are no longer deleted when new schedules are applied. A patch is introduced in this release to find and create placeholder schedules for these jobs.&lt;br /&gt;
* [[Built-in_Jobs#Job_History_Cleanup_Job|Job History Cleanup Job]] allowed configuration of maxAgeScheduleDays to be lower than maxAgeDays resulting in schedules being deleted for which there is still job history.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.2.0 === &lt;br /&gt;
Released April 2015. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[Cron#Recurrence|Recurrence]] job schedules.&lt;br /&gt;
* Upgrade to latest Java mail library.&lt;br /&gt;
* Support for determining user that initiated a [[Implementing_Jobs#Interruptable_Jobs|job interrupt]].&lt;br /&gt;
* Pickup time in [[Job Forking|forked jobs]] now reflects JVM startup to more accurately report execution times.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Adding new schedule with outstanding auto-retry execution no longer fails.&lt;br /&gt;
* Prevent license checks making extra calls.&lt;br /&gt;
* Ensure auto retries with 0 interval are executed as soon as possible, instead of after at least one minute.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.1.1 ===&lt;br /&gt;
Released April 2015. &lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fix critical bug where successful job runs after failures would be marked as failed due to job recovery handling.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.1.0 ===&lt;br /&gt;
Released March 2015. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* SQL Server 2008-2014 support.&lt;br /&gt;
* Improve event log messages.&lt;br /&gt;
* &amp;quot;?&amp;quot; now equivalent to &amp;quot;*&amp;quot; in cron schedules.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Prevent event log messages being dispatched before commit of source data.&lt;br /&gt;
* Fix delayed result set cleanup in job queuer in rare cases.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.0.1 ===&lt;br /&gt;
Released February 2015. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* Job schedule preview and update performance improvements.&lt;br /&gt;
* Job delete performance improvements for jobs with large number of history records.&lt;br /&gt;
* [[Implementing_Jobs#Classpath_Scanning|Classpath scanning]] interval now configurable (useful for job forking).&lt;br /&gt;
* [[Built-in_Jobs#Job_History_Cleanup_Job|JobHistoryCleanupJob]] now supports deleting old schedules.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Jobs running on hosts that are disabled during execution will now flag them as FAILED or COMPLETED as expected.&lt;br /&gt;
* Ensure identical job schedule states which butt against each other are merged.&lt;br /&gt;
* Set catalina.base property at Winstone startup so default log configuration works.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 3.0.0 ===&lt;br /&gt;
Released February 2015. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[Job Forking|Job forking]] added to support distinct JVM instances for each job execution.&lt;br /&gt;
* Full API expansion. APIs now support all functionality exposed by the admin web application. See [[REST Endpoints|REST API]] and [[Embedded API]] for details.&lt;br /&gt;
* Pretty URLs and login redirects in admin web application, including bookmark support.&lt;br /&gt;
* [[Clustering#Host_Preference|Host preference]] support.&lt;br /&gt;
* Full [[Initializing and Restoring|initialization and restoration]] support for Obsidian instances.&lt;br /&gt;
* Auto-polling for job history, logs and sent notifications views in admin web application.&lt;br /&gt;
* [[Admin_Custom_Calendars#Deleting|Custom calendar delete]] support. Also available in APIs.&lt;br /&gt;
* Convenience database [[Upgrading_Obsidian#Database_Upgrade_Scripts|upgrade]] and [[Getting_Started#Database_Creation_Scripts|create]] scripts included in installation bundle. Automatic version upgrades still supported.&lt;br /&gt;
* Better [[Implementing_Jobs#Inheritance|inheritance options]] for defined job parameters.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fix LAST recovery type when job left in READY state.&lt;br /&gt;
* Redirect to proper 404 and error pages.&lt;br /&gt;
* Optimize persistence calls to better handle large number of schedules per job.&lt;br /&gt;
* Fix LDAPAuthenticator group membership attribute lookup.&lt;br /&gt;
* Explicitly shut down daemon connection reaper thread at Obsidian shutdown.&lt;br /&gt;
* Fix minor bugs with schedule merges and splits.&lt;br /&gt;
* Fix warning shown in rare cases on job history listing.&lt;br /&gt;
* Minor help user interface fixes.&lt;br /&gt;
* Minor job chain user interface fixes.&lt;br /&gt;
* Prevent system parameter sort order changing on updates.&lt;br /&gt;
* Fix record duplicates in job history and corresponding APIs caused by multiple chains which have the same source and target jobs.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.9.1 === &lt;br /&gt;
Released November 2014. &lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fixed issue where auto retry caused failure during a Disabled window.&lt;br /&gt;
* Fixed filenames on screen data downloads.&lt;br /&gt;
* Fixed issue with Last recovery when last executed time to current time spanned a Disabled window.&lt;br /&gt;
* Graceful handling of host designator update interruptions.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.9.0 === &lt;br /&gt;
Released November 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[Conflicts#Jobs_in_Multiple_Conflict_Sets|Jobs in multiple conflict sets]].&lt;br /&gt;
* Add ad hoc flag to Context object supplied to SchedulableJob instances.&lt;br /&gt;
* Improved [[Admin_Job_Chains#Conditional_Chaining_Configuration|chain detail]] messages in [[Admin_Job_History_(3.x.x and earlier)#Job_History_Details|job history details]] table.&lt;br /&gt;
* Ability to set the host designator for multiple Obsidian instances in the same JVM via &amp;lt;code&amp;gt;com.carfey.obsidian.schedulerDesignation&amp;lt;/code&amp;gt; in the [[Advanced Configuration|properties file]].&lt;br /&gt;
* Job queuing performance improvements&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Ensure license check multiple is at least 2 in [[Admin_System_(3.x.x and earlier)|System]] tab.&lt;br /&gt;
* When recovering jobs which had died, new jobs were flagged as Overlapped instead of Missed.&lt;br /&gt;
* All recovery type resulting in Abandoned jobs incorrectly.&lt;br /&gt;
* Breadcrumbs incorrect when cloning a job.&lt;br /&gt;
* JNDI cannot be used when the key does not start with &amp;quot;java:&amp;quot; prefix. &amp;lt;ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using automated installer files from previous releases and are using JNDI, you may add &amp;lt;pre&amp;gt;&amp;lt;entry key=&amp;quot;database.jndi.name&amp;quot; value=&amp;quot;yourJNDIPath&amp;quot;/&amp;gt;&amp;lt;/pre&amp;gt; anywhere under the userInput element of UserInputPanel.3 and empty out the value for &#039;&#039;&#039;database.url&#039;&#039;&#039;. For example,&lt;br /&gt;
  &amp;lt;com.izforge.izpack.panels.UserInputPanel id=&amp;quot;UserInputPanel.3&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;userInput&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;db.schema&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&#039;&#039;&amp;lt;entry key=&amp;quot;database.url&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;database.password&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;database.table.prefix&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;database.connection.timeout&amp;quot; value=&amp;quot;2000&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;database.username&amp;quot; value=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&#039;&#039;&amp;lt;entry key=&amp;quot;database.jndi.name&amp;quot; value=&amp;quot;java:comp/env/jdbc/obsidianJNDI&amp;quot;/&amp;gt;&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;database.max.connections&amp;quot; value=&amp;quot;40&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;include.all.jdbc&amp;quot; value=&amp;quot;false&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;/userInput&amp;gt;&lt;br /&gt;
  &amp;lt;/com.izforge.izpack.panels.UserInputPanel&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/ref&amp;gt;&lt;br /&gt;
* Database connections not rolled back just before close when removed from the pool, resulting in warning log messages.&lt;br /&gt;
* Sanitize HTTP headers, email subjects, etc. to prevent injection issues.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.8.1 === &lt;br /&gt;
Released September 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* New connection pool reaper to draw down pool size if usage allows.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* More explicit handling of connection pool emptying on Obsidian shutdown.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.8.0 === &lt;br /&gt;
Released September 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* Login popup when session expires in [[Admin_Web_Application_Guide|admin web application]]. Users can now log back in without losing their changes.&lt;br /&gt;
* Support for using [[Advanced_Configuration|context classloader]] via configuration property &#039;&#039;com.carfey.jdk.useContextClassLoader&#039;&#039;.&lt;br /&gt;
* Reduced number of connections required for normal operation. Previously, up to 1 connection per job per minute was required.&lt;br /&gt;
* Minor user interface improvements.&lt;br /&gt;
* Support for re-queuing jobs rejected from the job thread pool (via parameter &#039;&#039;queueJobsOnPoolRejection&#039;&#039;).&lt;br /&gt;
* Improved default log4j rolling file appender configuration.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fix NullPointerException on job recovery when last job was rejected by the thread pool.&lt;br /&gt;
* Fix rare instances of licenses not being released due to token file issues&lt;br /&gt;
* Fix poor validation message on saving if invalid boolean system parameter.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.7.1 === &lt;br /&gt;
Released August 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[REST_API#Authentication_.26_Authorization|REST authentication]] can be configured to be optional.&lt;br /&gt;
* Cron pattern expansion. Support range increments (e.g. 3-20/5)&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fix NullPointerException in handling mdoodood..dkididkdkd&lt;br /&gt;
IP changes.&lt;br /&gt;
* Upgraded to TLD 2.0 fixing Obsidian WAR deployment in JBoss 6.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.7.0 === &lt;br /&gt;
Released July 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[Admin_Notifications#Job_Execution_Subscriptions|Re-tooled job execution notifications]], including [[Admin_Notifications#Conditional_Job_Notification_Configuration|conditional notifications]]&lt;br /&gt;
* We have open-sourced all of our [[Built-in_Jobs | built-in jobs]]!&lt;br /&gt;
* File-based job [[Implementing_Jobs#Initializing_Job_Schedules|initialization]].&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fix page jumping when modifying job chain conditions on long pages.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.6.1 === &lt;br /&gt;
Released June 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* Cygwin support in startup/shutdown scripts.&lt;br /&gt;
* Run parameters override configured parameters.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Oracle CLOB field storage fix.&lt;br /&gt;
* Fix single-node mode on host with no recognizable NIC.&lt;br /&gt;
* Prevent currently or soon to be running jobs from being deleted.&lt;br /&gt;
* Fix multiple job history parameters failing on H2.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.6.0 ===&lt;br /&gt;
Released May 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[Implementing_Jobs#Classpath_for_Building_and_Deploying|Job implementation metadata stored in database]]. Admin UI deployments no longer require Job Implementations on the classpath in order to schedule and configure.&lt;br /&gt;
* Delete Chain support in [[Admin_Job_Chains#Deleting|UI]], [[Embedded_API#Delete_a_Job_Chain|Embedded API]] and [[REST_Endpoints#DELETE_an_existing_job_chain|REST API]]&lt;br /&gt;
* Hardware-linked licensing supported in public Obsidian bundle.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fixed [[Implementing_Jobs#Classpath_Scanning|Classpath Scanning]] to work in WebLogic 12c.&lt;br /&gt;
* Fixed chain recovery date ranges being overly broad.&lt;br /&gt;
* Ensure any stale timezone records are cleaned up.&lt;br /&gt;
* Removed some libraries stored multiple times in installer.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.5.1 ===&lt;br /&gt;
Released April 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* Expose convenience JobId and Nickname getters in [[Implementing_Jobs#SchedulableJob_Interface|Context]].&lt;br /&gt;
* Improved performance of chaining recovery.&lt;br /&gt;
* [[Installation_Guide#Configuring_3rd_Party_Library_Conflict_Management|JarJar]] bytecode modification support now includes gson.&amp;lt;ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using automated installer files from previous releases, you may add &amp;lt;pre&amp;gt;&amp;lt;entry key=&amp;quot;jarjar.gson&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;&amp;lt;/pre&amp;gt; or &amp;lt;pre&amp;gt;&amp;lt;entry key=&amp;quot;jarjar.gson&amp;quot; value=&amp;quot;false&amp;quot;/&amp;gt;&amp;lt;/pre&amp;gt; according to your desired usage anywhere under the userInput element of UserInputPanel.14. For example,&lt;br /&gt;
 &amp;lt;com.izforge.izpack.panels.UserInputPanel id=&amp;quot;UserInputPanel.14&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;userInput&amp;gt;&lt;br /&gt;
        &#039;&#039;&#039;&#039;&#039;&amp;lt;entry key=&amp;quot;jarjar.gson&amp;quot; value=&amp;quot;false&amp;quot;/&amp;gt;&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;jarjar.jxl&amp;quot; value=&amp;quot;false&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;jarjar.dom4j&amp;quot; value=&amp;quot;false&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;jarjar.jmustache&amp;quot; value=&amp;quot;false&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;jarjar.opencsv&amp;quot; value=&amp;quot;false&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;entry key=&amp;quot;jarjar.h2&amp;quot; value=&amp;quot;false&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;/userInput&amp;gt;&lt;br /&gt;
 &amp;lt;/com.izforge.izpack.panels.UserInputPanel&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/ref&amp;gt;&lt;br /&gt;
* Introduced support for having a scheduler and non-scheduler instance running in same class-space.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Improved failure messaging when a scheduler node is started in a different time zone than the current nodes in the cluster.&lt;br /&gt;
* Chain search filters aren&#039;t saved.&lt;br /&gt;
* Exclude unneeded web jars from standalone installations.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.5.0 === &lt;br /&gt;
Released March 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* [[Admin_Jobs#Execution_.26_Pickup|Retry Interval]] support for Auto Retry of failed jobs.&lt;br /&gt;
* [[Admin_Global_Parameters|Global Job Parameter]] support.&lt;br /&gt;
* [[Installation_Guide#JNDI_Configuration|JNDI Support]] for Database Connections and Email Sessions.&lt;br /&gt;
* MariaDB 5.5 support.&lt;br /&gt;
* JBoss AS 7 support.&lt;br /&gt;
* Text filter on Job Chain View.&lt;br /&gt;
* [[Installation_Guide#Configuring_3rd_Party_Library_Conflict_Management|JarJar]] bytecode modification support during installation to avoid dependency conflicts.&lt;br /&gt;
* Embedded API Javadoc artifact included in installation.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Support */2 cron expressions, [[Admin_Jobs#Edit.2C_View_.26_Change_Schedules|Schedule Inputs]] extended to 255 characters.&lt;br /&gt;
* REST API - [[REST_Endpoints#GET_a_list_of_jobs|JobListing]].&amp;lt;code&amp;gt;getJobs()&amp;lt;/code&amp;gt; was returning &amp;lt;code&amp;gt;List&amp;amp;lt;Job&amp;amp;gt;&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;List&amp;amp;lt;JobState&amp;amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Support multiple keys in key server proxy. &lt;br /&gt;
* Support VFS URLs in [[Implementing_Jobs#Classpath_Scanning|Classpath Scanner]] for JBoss.&lt;br /&gt;
* Postgres failed on blank schema.&lt;br /&gt;
* Job resubmission validates that job isn&#039;t disabled for next minute instead of current minute.&lt;br /&gt;
* Embedded API - [[Embedded_API#Delete_a_Job|Delete Job]] would fail if job class has since been removed from classpath.&lt;br /&gt;
* [[Admin_Jobs#Job_Listing|Job Listing]] schedule/state defaults to next minute.&lt;br /&gt;
* Filters aren&#039;t saved consistently on search screens and never saved for runtime preview.&lt;br /&gt;
* Table settings aren&#039;t saved on [[Admin_Jobs#Job_Listing|Job Listing]].&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.4.1 === &lt;br /&gt;
Released February 2014. &lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fix installer not working in console mode.&lt;br /&gt;
* Ensure that defined job parameters and their values are returned in the original configured order.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.4.0 === &lt;br /&gt;
Released February 2014. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* New [[Getting_Started|graphical installer and simplified startup scripts]].&lt;br /&gt;
* Full support for job chaining and conflicts in the [[REST API]] and [[Embedded API]].&lt;br /&gt;
* Revamped WebSphere support (8.5).&lt;br /&gt;
* [[Scripting Jobs]] can now be configured with a script file path instead of the script itself.&lt;br /&gt;
* New [[Built-in_Jobs#MySqlBackupJob|MySqlBackupJob]] built-in job.&lt;br /&gt;
* Informational message logged when a new version of Obsidian is available.&lt;br /&gt;
* [[Admin_User_Management#Deleting_Users|Delete user]] support.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Fixed job execution recovery not working in some cases when the database is temporarily unavailable.&lt;br /&gt;
* Fix PostgreSQL connections not released from pool properly when connection is invalid.&lt;br /&gt;
* Fix [[Implementing_Jobs#Classpath_Scanning|Classpath Scanning]] on WebSphere.&lt;br /&gt;
* Fix UI rendering issue on Jobs screen on WebSphere.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.3.1 === &lt;br /&gt;
Released December 2013. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* New [[Built-in_Jobs#Script_Job|Script File Job]]&lt;br /&gt;
* Improved responsiveness of job interrupts for built-in jobs.&lt;br /&gt;
* UI tweak to [[Jobs]] screen to avoid displaying custom calendars when not in use.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Update default log configuration for Tomcat to use &amp;lt;code&amp;gt;catalina.base&amp;lt;/code&amp;gt;, which works consistently on *nix platforms.&lt;br /&gt;
* Remove redundant event logging for job success and failure in the Job Spawner category.&lt;br /&gt;
* Handle releasing licenses even when temp directory is not writable, and attempt to use working directory as a fallback.&lt;br /&gt;
* Job names containing ampersands did not work as filters in search screens.&lt;br /&gt;
* Host designator assigned earlier in startup to ensure logging accurately reports the host.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.3.0 === &lt;br /&gt;
Released November 2013. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* Fully unified [[Embedded API]] which contains same operations and has same semantics as the [[REST API]]. The previous Legacy API is now deprecated.&lt;br /&gt;
* [[REST API]] bean classes bundled in &amp;lt;code&amp;gt;obsidian.jar&amp;lt;/code&amp;gt; for simplified Java integration.&lt;br /&gt;
* Submission of [[Admin_Jobs#Ad_Hoc_.26_One-Time_Run_Submission|one-time job runs]] via the UI now allows specifying exact minutes instead of 5 minute intervals.&lt;br /&gt;
* [[Chaining]] evaluation is now fully recoverable. Processing of chaining now processed independently of job execution.&lt;br /&gt;
* Dozens of UI tweaks and improvements.&lt;br /&gt;
* Stale hosts in [[Systems|Systems tab]] now are pruned after about an hour of inactivity. Host disabling is preserved if the node is restored.&lt;br /&gt;
* Graceful shutdown in Winstone for [[Getting Started|quick start]] method.&lt;br /&gt;
* [[Jobs#Custom_Calendars|Custom Calendars]] can now be specified with multiple delimiters, including new lines and spaces.&lt;br /&gt;
* [[Email Templates]] can now include the event category.&lt;br /&gt;
* Track most recent upgraded version in the database.&lt;br /&gt;
* Job runtime [[REST_Endpoints#GET_a_list_of_scheduled_runtimes_.28supports_multiple_jobs.29|REST endpoint]] now defaults to a more useful previous 24-hour window.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Chain All setting not shown correctly when editing existing [[Jobs]].&lt;br /&gt;
* Abandoned, Missed and Conflict Missed chaining never fires.&lt;br /&gt;
* Current scheduled state not shown correctly for existing jobs in [[Jobs]] in Winstone.&lt;br /&gt;
* Export on [[Logs]] contained extra text in ID column.&lt;br /&gt;
* Chained jobs now respect host restrictions.&lt;br /&gt;
* Correct custom calendar reversed PUT/POST for [[REST API]], and implement standardized date formatting.&lt;br /&gt;
* UI table listings no longer flash error message when quickly navigating away during a request.&lt;br /&gt;
* Legacy API calls no longer incorrectly start non-daemon threads for notifications and event logging.&lt;br /&gt;
* Lock issues when evaluating many [[Chaining|chained jobs]].&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.2.1 ===&lt;br /&gt;
Released October 2013. &lt;br /&gt;
==== Features / Enhancements ==== &lt;br /&gt;
* Enhanced [[Spring_Integration|Spring Integration]] to remove need for distinct [[Implementing_Jobs#Classpath_Scanning|Classpath Scanning]]. Jobs can be auto detected from Spring context. &lt;br /&gt;
* Enhanced [[Job_Features#Host_Restrictions_or_Affinity|Running Hosts Restrictions]]. Now a configuration value can be used to determine if these restrictions apply to Ad Hoc jobs. &lt;br /&gt;
* Ability to [[Getting_Started#Disabling_Automatic_Database_Updates|disable database updates]] on startup. &lt;br /&gt;
* Add helpful text to job screen indicating you can type in a job class Obsidian &lt;br /&gt;
* Additional Spring support for [[Spring_Integration#Wiring_an_Embedded_Obsidian_Scheduler|embedded schedulers]].&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ==== &lt;br /&gt;
* Corrected issue where classpath scanning configuration values were being overridden back to default on each restart (introducted in 2.1). &lt;br /&gt;
* Fix SchedulerStarter shutting down JVM in embedded mode.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.2.0 ===&lt;br /&gt;
Released October 2013.&lt;br /&gt;
==== Features / Enhancements ====&lt;br /&gt;
* [[Spring_Integration|Spring]] Dependency Injection support.&lt;br /&gt;
* [[Built-in_Jobs#File_Scanner_Job|File scanner]] job.&lt;br /&gt;
* [[Advanced_Configuration#Dependent_Libraries|Python and Groovy]] libs updated. Python from jython.jar (version 2.5.2rc2) to jython-standalone-2.5.3.jar. Groovy from groovy-all-1.7.6.jar to groovy-all-2.1.8.jar.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* DBAuthenticator is now defaulted configuration. Improved handling of bad configuration on startup.&lt;br /&gt;
* Obsidian web app startup no longer attempts to start the scheduler with certain types of unrecoverable bad configuration.&lt;br /&gt;
* Fixed issue where classpath scanner only worked in Admin Web Application if running with an embedded scheduler.&lt;br /&gt;
* Fixed issue where Auto Failure Retry combined with triggered Job Chaining resulted in chaining failure.&lt;br /&gt;
* Cleaned up Job History Search Results File Export that could contain garbage characters.&lt;br /&gt;
* Fixed issue where job runtime preview fails when only disabled job state exists.&lt;br /&gt;
* Fixed TRACE logging level - No logging output if level explicitly set to TRACE.&lt;br /&gt;
* Remove unnecessary JAR files bundled with the installation package.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.1.1 ===&lt;br /&gt;
Released September 2013.&lt;br /&gt;
==== Features / Enhancements ====&lt;br /&gt;
* [[Admin_Jobs#Run_Parameters|Ad Hoc / One-Time Run Job Parameterization]].&lt;br /&gt;
* Obsidian license purchases via Stripe.&lt;br /&gt;
* All Carfey Software libraries bundled into single obsidian.jar.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* Postgres schema support - only supported public schema.&lt;br /&gt;
* LogCleanupJob does not validate specified LEVEL attribute.&lt;br /&gt;
* LDAPAuthenticator assumed &#039;&#039;cn&#039;&#039; in building &#039;&#039;dn&#039;&#039; for authentication. Now configurable - see [[Advanced_Configuration#Configuration_Details|Advanced Configuration]].&lt;br /&gt;
* LDAPAuthenticator assumed only &#039;&#039;uniqueMember&#039;&#039; for group membership. Now also checks other common attributes, supporting ActiveDirectory. See [[Authenticator#Customizing_our_LDAP_Authenticator|LDAP Authenticator]].&lt;br /&gt;
* &#039;&#039;run.obsidian&#039;&#039; target execution classpath included in Obsidian bundle includes non-jar/non-resource files. &lt;br /&gt;
* Site license verification runs once on startup instead of every lease cycle.&lt;br /&gt;
* Date parsing issue related to Locales on retrieving and refreshing license. Fixed in [http://sourceforge.net/projects/carfeydate/ release 1.2 of carfey-date].&lt;br /&gt;
* In rare cases, job results in Job History listing could be mangled.&lt;br /&gt;
* Proxy key server would fail on requests when run from time zones different from the master key server.&lt;br /&gt;
* Oracle job results limited to 2000 characters - now unlimited on all platforms.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.1 ===&lt;br /&gt;
Released July 2013.&lt;br /&gt;
==== Features / Enhancements ====&lt;br /&gt;
* [[Obsidian_Tables|Table name prefix]] support.&lt;br /&gt;
* [[Licenses &amp;amp; Nodes|Site license]] support in public Obsidian bundle.&lt;br /&gt;
* [[Advanced_Configuration#Properties_File|Programmatic properties]] override support.&lt;br /&gt;
* Alternate [[Advanced_Configuration#Configuration_Details|schema user/owner]] support. Additional Oracle details [[Obsidian_Tables#Oracle_Privileges|here]].&lt;br /&gt;
* [[Admin_Notifications#Deleting_a_Subscriber|Delete subscriber]] support.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* Ensure all [[Admin_Logs|Event Log]] messaging to always use Display Name for enums.&lt;br /&gt;
* Expand script job disabling to disable all potentially dangerous jobs.&lt;br /&gt;
* Ensure internal class AnnotatedSchedulableJob is always excluded from available class listing in [[Admin_Jobs#Job_Nickname_and_Class|Job Admin]].&lt;br /&gt;
* Upgraded to more recent Oracle JDBC driver (11.2.0.3)&lt;br /&gt;
* Various database metadata optimizations&lt;br /&gt;
* [[Implementing_Jobs#Classpath_Scanning|Classpath scanner]] rescans on parameter changes.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 2.0 ===&lt;br /&gt;
Released June 2013.&lt;br /&gt;
==== Features / Enhancements ====&lt;br /&gt;
* [[Implementing_Jobs#Classpath_Scanning|Classpath Scanning]] to automatically populate Job UI with available jobs.&lt;br /&gt;
* Fully customizable [[Email_Templates|Email Templates]].&lt;br /&gt;
* New [[Embedded_API|Embedded API]] operations exposed so job configuration can be manipulated without REST API or UI.&lt;br /&gt;
* [[Admin_Jobs#Custom_Calendars|Custom Calendar]] support to add date-based exclusions to job schedules (e.g. holidays).&lt;br /&gt;
* New [[Admin_User_Management#User_Rights|Limited Read]] role which restricts access to potentially sensitive configuration.&lt;br /&gt;
* New [[Built-in_Jobs|Built-in Jobs]] - file archival and file clean up.&lt;br /&gt;
* Easy [[Admin_Jobs#Job_Listing|job cloning]] in UI.&lt;br /&gt;
* [[Built-in_Jobs#Shell_Script_Jobs|Shell scripting]] job support.&lt;br /&gt;
* Additional [[Admin_Jobs#Job_Edit_Screen|job execution options]] - auto-retries on failure and new &amp;quot;Chain All&amp;quot; chaining mode.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* Improve logging of some trapped exceptions.&lt;br /&gt;
* Improve handling of spawn locks to reduce contention.&lt;br /&gt;
* Fix standalone execution from Ant script.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 1.5.2 ===&lt;br /&gt;
Released May 2013.&lt;br /&gt;
==== Features / Enhancements ====&lt;br /&gt;
* Job nickname lengthened from 50 to 255 characters to allow more specific nicknames.&lt;br /&gt;
* Database metadata loading restricted to known Obsidian application tables.&lt;br /&gt;
* Add support in [[REST_Endpoints#Job_Endpoints|job listing endpoint]] for nickname and job custom parameter tag search.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* Fixed JobHistoryCleanupJob failures that occurred when a record to be cleaned up had been interrupted.&lt;br /&gt;
* Disallow failed job resubmission if the job is currently running. This prevents these resubmissions from potentially being ABANDONED due to execution duration exceeding pickup buffer minutes specified on the job. Job is available for resubmission once currently running job completes.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 1.5.1 ===&lt;br /&gt;
Released February 2013.&lt;br /&gt;
==== Features / Enhancements ====&lt;br /&gt;
* [[Implementing_Jobs#Interruptable_Jobs|Interruptable Jobs]], including new [[REST_API|REST API]] endpoint.&lt;br /&gt;
* Spawning improvements to seek minute boundaries more consistently.&lt;br /&gt;
* Runtime preview now defaults to start time to current time.&lt;br /&gt;
* Improved license handling for paid licenses to increase reliability in case of outages.&lt;br /&gt;
* A handful of minor UI tweaks and additions.&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* Fix connection handling when database connectivity is lost in spawner or queuer threads.&lt;br /&gt;
* Fix criteria causing some running conflicts to be not found resulting in conflicted jobs running concurrently.&lt;br /&gt;
* Improve messaging and details when failing to connect to MySQL.&lt;br /&gt;
* Various UI fixes.&lt;br /&gt;
* Events for jobs would sometimes be dispatched for the wrong target.&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 1.5 ===&lt;br /&gt;
Released January 2013.&lt;br /&gt;
==== Features / Enhancements ====&lt;br /&gt;
* [[REST_API|REST API]] for a complete range of job, scheduling and host management features.&lt;br /&gt;
* Export support in Excel, CSV and XML in [[Admin_Job_History_(3.x.x and earlier)#Exporting_Results|Job History]], [[Admin_Jobs#Exporting_Results|Job]], [[Admin_Job_Run_Time_Preview|Job Runtimes]], [[Admin_Logs#Exporting_Results|Log]] and [[Admin_Notifications#Sent_Notifications_Screen|Sent Notifications]] views.&lt;br /&gt;
* Expected job run time parameters - [[Admin_Jobs#Execution_.26_Pickup|See Expected  Length]].  Events trigger in cases where execution duration is outside of defined threshold.&lt;br /&gt;
* Display actual run duration in job history view&lt;br /&gt;
* Enhance run once future date pattern to match actual future date.  Contributes to consistency in job state absorption in new overlapping state ranges.&lt;br /&gt;
* Allow single node to run without valid lease&lt;br /&gt;
* Add 90/120 minutes pickup buffer option&lt;br /&gt;
&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* Ad hoc submission validation text inconsistent&lt;br /&gt;
* Job set to ad hoc active state would not allow one-time immediate submissions.&lt;br /&gt;
* Workaround String indexoutofbounds issue with winstone server&lt;br /&gt;
* Read-only users can submit one-time job runs.&lt;br /&gt;
* Can&#039;t save unscheduled active job in postgresql&lt;br /&gt;
* After saving new user, help icon disappears&lt;br /&gt;
* MySQL - Audit columns hit truncation error when registered user is too long&lt;br /&gt;
* Various display bugs&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 1.4.2 ===&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* Lock wait timeout fix&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 1.4.1 ===&lt;br /&gt;
==== Features / Enhancements ====&lt;br /&gt;
* Conditional [[Chaining]] support.&lt;br /&gt;
* [[Admin_Jobs#Deleting|Delete]] job support.&lt;br /&gt;
* [[Admin_Jobs#Job_Listing|Job]] view filter inclusivity those with only future states.&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* Default initial sorts case insenstive&lt;br /&gt;
* Status scheduling allowed for gaps&lt;br /&gt;
* Failed conditional chain prevents scheduling of job&lt;br /&gt;
* Schedule preview isn&#039;t ordered by effective date&lt;br /&gt;
* JobHistory order by clause sometimes ignored&lt;br /&gt;
&lt;br /&gt;
=== Obsidian 1.4 ===&lt;br /&gt;
==== Features / Enhancements ====&lt;br /&gt;
* View of saved job history [[Admin_Job_History_(3.x.x and earlier)#Job_History_Details|results]]. &lt;br /&gt;
* Add schedule shortcut link on Job view&lt;br /&gt;
==== Bug Fixes ====&lt;br /&gt;
* Various display bugs&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Footnotes ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=REST_Endpoints&amp;diff=4157</id>
		<title>REST Endpoints</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=REST_Endpoints&amp;diff=4157"/>
		<updated>2026-07-31T14:49:58Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page documents the format of each available REST endpoint.&lt;br /&gt;
&lt;br /&gt;
For information on data formats, valid enumerations values, common behaviour and more, see the primary [[REST API]] page.&lt;br /&gt;
&lt;br /&gt;
All endpoints have corresponding bean classes that can be used with JSON object mappers like [https://github.com/google/gson Gson]. If you wish to use these, please review [[REST_API#JSON_Bean_Classes|bean classes]] for information on serialization of Obsidian&#039;s custom types.&lt;br /&gt;
&lt;br /&gt;
= Job Endpoints =&lt;br /&gt;
&lt;br /&gt;
==GET a list of jobs==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/jobs[?host=host1&amp;amp;activeStatus=ENABLED&amp;amp;nickname=jobname&amp;amp;param_group=orders&amp;amp;jobClass=com.example.ExportJob]&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns a list of configured jobs, optionally filtered by query string parameters.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Query String Parameters&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|-&lt;br /&gt;
| activeStatus || N || Restricts the preview to the selected statuses. See [[Unified_API#Enumerations|Enumerations]] for valid values. Supports multiple values.&lt;br /&gt;
|-&lt;br /&gt;
| effectiveDate || N || If querying by activeStatus, this allows you to indicate what point in time to compare against the job status. Defaults to next minute.&lt;br /&gt;
|-&lt;br /&gt;
| host || N || If specified, only jobs that run on the specified host name(s) are included. Supports multiple values.&lt;br /&gt;
|-&lt;br /&gt;
| nickname || N || If specified, only jobs matching the supplied nickname are returned. Wildcards may be included to support partial matches by using %, or exact literals can be used. For example, to find all jobs containing the word &amp;quot;order&amp;quot;, use &amp;quot;%order%&amp;quot;. Supports multiple values.&lt;br /&gt;
|-&lt;br /&gt;
| jobClass || N || If specified, only jobs matching the supplied job class are returned. Wildcards may be included to support partial matches by using %, or exact literals can be used. For example, to find all jobs with job classes containing the word &amp;quot;Export&amp;quot;, use &amp;quot;%export%&amp;quot;. Supports multiple values. &#039;&#039;Available from version 3.4.0 forward.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| folder || N || If specified, only jobs matching the supplied folders are returned. If a parent path is supplied, all jobs containing that path or subpaths are included in the results. If an empty string is supplied, jobs with no folder will be returned. Supports multiple values. &#039;&#039;Available from version 4.1.0 forward.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| param_&#039;&#039;parameterName&#039;&#039; || N || If specified, values starting with &#039;&#039;param_&#039;&#039; can be used to match only on jobs with specific job parameter values, either custom or defined. If multiple values for the same query parameter starting with &#039;&#039;param_&#039;&#039; are supplied, a job is matched if any of its configured values match one of the supplied values. If &#039;&#039;param_&#039;&#039; filters with separate names are used, each must have a matching value for the job to be returned.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Usage note:&#039;&#039;&#039; This can be used to tag jobs with searchable metadata by configuring custom parameters. For example, if jobs belong to logical groups, you may create a custom parameter on applicable jobs named &amp;quot;group&amp;quot; and use a query like the following to find jobs belonging to the &amp;quot;customer&amp;quot; or &amp;quot;order&amp;quot; groups:&lt;br /&gt;
&amp;lt;code&amp;gt;GET http(s)://localhost/rest/jobs[?param_group=customer&amp;amp;param_group=order]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;jobs&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
      &amp;quot;jobId&amp;quot;: 33,&lt;br /&gt;
      &amp;quot;pickupBufferMinutes&amp;quot;: 5,&lt;br /&gt;
      &amp;quot;nickname&amp;quot;: &amp;quot;jobOne&amp;quot;,&lt;br /&gt;
      &amp;quot;folder&amp;quot;: &amp;quot;Production/Test&amp;quot;, // as of 4.1.0&lt;br /&gt;
      &amp;quot;interruptable&amp;quot;: false, // indicates if job can be interrupted&lt;br /&gt;
      &amp;quot;autoRetryCount&amp;quot;: 0, // indicates number of auto-retry attempts to be made on failure&lt;br /&gt;
      &amp;quot;autoRetryInterval&amp;quot;: 0, // indicates auto-retry minimum interval in minutes&lt;br /&gt;
      &amp;quot;autoRetryIntervalExponent&amp;quot;: false, // indicates whether to exponentially increase the interval between auto retries&lt;br /&gt;
      &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
      &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
      &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
      &amp;quot;chainAll&amp;quot; : true, // corresponds to chainAll setting on job creation&lt;br /&gt;
      &amp;quot;activeSchedule&amp;quot;: {&lt;br /&gt;
        &amp;quot;jobScheduleId&amp;quot;: 34,&lt;br /&gt;
        &amp;quot;status&amp;quot;: &amp;quot;CHAIN_ACTIVE&amp;quot;,&lt;br /&gt;
        &amp;quot;endDate&amp;quot;: &amp;quot;2999-12-31T23:59:00-0800&amp;quot;,&lt;br /&gt;
        &amp;quot;effectiveDate&amp;quot;: &amp;quot;2013-01-06T14:37:00-0800&amp;quot;,&lt;br /&gt;
        &amp;quot;customCalendarId&amp;quot;: 123 // if configured, the custom calendar&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.JobHistoryCleanupJob&amp;quot;,&lt;br /&gt;
      &amp;quot;revision&amp;quot;: 0&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;recoveryType&amp;quot;: &amp;quot;CONFLICTED&amp;quot;,&lt;br /&gt;
      &amp;quot;jobId&amp;quot;: 35,&lt;br /&gt;
      &amp;quot;pickupBufferMinutes&amp;quot;: 123,&lt;br /&gt;
      &amp;quot;nickname&amp;quot;: &amp;quot;jobTwo&amp;quot;,&lt;br /&gt;
      &amp;quot;interruptable&amp;quot;: false&lt;br /&gt;
      &amp;quot;autoRetryCount&amp;quot;: 0, // indicates number of auto-retry attempts to be made on failure&lt;br /&gt;
      &amp;quot;autoRetryInterval&amp;quot;: 0, // indicates auto-retry minimum interval in minutes&lt;br /&gt;
      &amp;quot;autoRetryIntervalExponent&amp;quot;: false, // indicates whether to exponentially increase the interval between auto retries&lt;br /&gt;
      &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
      &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
      &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
      &amp;quot;chainAll&amp;quot; : true, // corresponds to chainAll setting on job creation&lt;br /&gt;
      &amp;quot;activeSchedule&amp;quot;: {&lt;br /&gt;
        &amp;quot;jobScheduleId&amp;quot;: 37,&lt;br /&gt;
        &amp;quot;status&amp;quot;: &amp;quot;ENABLED&amp;quot;,&lt;br /&gt;
        &amp;quot;schedule&amp;quot;: &amp;quot;@daily&amp;quot;,&lt;br /&gt;
        &amp;quot;scheduleDescription&amp;quot;: &amp;quot;At midnight every day&amp;quot; (as of 5.2.0)&lt;br /&gt;
        &amp;quot;endDate&amp;quot;: &amp;quot;2013-01-08T14:34:00-0800&amp;quot;,&lt;br /&gt;
        &amp;quot;effectiveDate&amp;quot;: &amp;quot;2013-01-07T14:34:00-0800&amp;quot;,&lt;br /&gt;
        &amp;quot;customCalendarId&amp;quot;: 123 // if configured, the custom calendar&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.LogCleanupJob&amp;quot;,&lt;br /&gt;
      &amp;quot;revision&amp;quot;: 2&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GET details of an existing job==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/jobs/{jobId}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobDetail&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns full job information, including all historical schedules and parameter information. Returns a 404 if not found.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;schedules&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;effectiveDate&amp;quot;: &amp;quot;2013-01-08T15:15:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;endDate&amp;quot;: &amp;quot;2031-04-30T07:59:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;status&amp;quot;: &amp;quot;ENABLED&amp;quot;,&lt;br /&gt;
      &amp;quot;schedule&amp;quot;: &amp;quot;* * * * *&amp;quot;,&lt;br /&gt;
      &amp;quot;scheduleDescription&amp;quot;: &amp;quot;Every minute every day&amp;quot; (as of 5.2.0)&lt;br /&gt;
      &amp;quot;jobScheduleId&amp;quot;: 35,&lt;br /&gt;
      &amp;quot;customCalendarId&amp;quot;: 123 // if configured, the custom calendar&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;effectiveDate&amp;quot;: &amp;quot;2031-04-30T08:00:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;endDate&amp;quot;: &amp;quot;2031-04-30T08:00:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;schedule&amp;quot;: &amp;quot;0 8 30 4 3#5&amp;quot;,&lt;br /&gt;
      &amp;quot;scheduleDescription&amp;quot;: &amp;quot;At 8:00AM on the 30th during April on the 3rd Friday&amp;quot; (as of 5.2.0)&lt;br /&gt;
      &amp;quot;status&amp;quot;: &amp;quot;ENABLED&amp;quot;,&lt;br /&gt;
      &amp;quot;jobScheduleId&amp;quot;: 2952,&lt;br /&gt;
      &amp;quot;parameters&amp;quot;: [{&lt;br /&gt;
            &amp;quot;value&amp;quot;: &amp;quot;value&amp;quot;,&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;paramName&amp;quot;,&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;STRING&amp;quot;,&lt;br /&gt;
            &amp;quot;values&amp;quot;: [&amp;quot;value&amp;quot;]&lt;br /&gt;
      }]&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;effectiveDate&amp;quot;: &amp;quot;2015-01-06T15:53:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;endDate&amp;quot;: &amp;quot;2016-01-06T15:53:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;status&amp;quot;: &amp;quot;AD_HOC_ACTIVE&amp;quot;,&lt;br /&gt;
      &amp;quot;jobScheduleId&amp;quot;: 38&lt;br /&gt;
    }&lt;br /&gt;
  ],&lt;br /&gt;
  &amp;quot;currentJobScheduleId&amp;quot;: 35, // id of the item in &amp;quot;schedules&amp;quot; which is active right now&lt;br /&gt;
  &amp;quot;jobClassDescription&amp;quot;: &amp;quot;This job cleans up log history beyond the configured age.&amp;quot;, // returned only if Job is annotated with @Description&lt;br /&gt;
  &amp;quot;hosts&amp;quot;: [&lt;br /&gt;
    &amp;quot;host1&amp;quot;&lt;br /&gt;
  ],&lt;br /&gt;
  &amp;quot;job&amp;quot;: {&lt;br /&gt;
    &amp;quot;jobId&amp;quot;: 34,&lt;br /&gt;
    &amp;quot;revision&amp;quot;: 0,&lt;br /&gt;
    &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.LogCleanupJob&amp;quot;,&lt;br /&gt;
    &amp;quot;nickname&amp;quot;: &amp;quot;testCreateWithEffectiveDatesAndParams&amp;quot;,&lt;br /&gt;
     &amp;quot;folder&amp;quot;: &amp;quot;Production/Test&amp;quot;, // as of 4.1.0&lt;br /&gt;
    &amp;quot;pickupBufferMinutes&amp;quot;: 5,&lt;br /&gt;
    &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
    &amp;quot;interruptable&amp;quot;: false, // indicates if job can be interrupted (as 1.5.1)&lt;br /&gt;
    &amp;quot;autoRetryCount&amp;quot;: 0, // indicates number of auto-retry attempts to be made on failure&lt;br /&gt;
    &amp;quot;autoRetryInterval&amp;quot;: 0, // indicates auto-retry minimum interval in minutes&lt;br /&gt;
    &amp;quot;autoRetryIntervalExponent&amp;quot;: false, // indicates whether to exponentially increase the interval between auto retries&lt;br /&gt;
    &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
    &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
    &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
    &amp;quot;chainAll&amp;quot; : true // corresponds to chainAll setting on job creation&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;parameters&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;level&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;STRING&amp;quot;,&lt;br /&gt;
      &amp;quot;allowMultiple&amp;quot;: true,&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;values&amp;quot;: [ &amp;quot;WARN&amp;quot;, &amp;quot;ERROR&amp;quot; ], // Values may contain global parameter references&lt;br /&gt;
      &amp;quot;defaultValue&amp;quot;: &amp;quot;ALL&amp;quot;,&lt;br /&gt;
      &amp;quot;defined&amp;quot;: true // true if defined by @Configuration annotation on the job&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;maxAgeDays&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;INTEGER&amp;quot;, // see Enumerations above for valid values&lt;br /&gt;
      &amp;quot;allowMultiple&amp;quot;: false,&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;values&amp;quot;: [ &amp;quot;60&amp;quot; ], // values is always a list for consistency, even when allowMultiple is false&lt;br /&gt;
      &amp;quot;defaultValue&amp;quot;: &amp;quot;120&amp;quot;,&lt;br /&gt;
      &amp;quot;defined&amp;quot;: true&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==POST a new job==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;POST http(s)://localhost/rest/jobs&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobCreationRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobDetail&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Creates a new job with an initial schedule.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.LogCleanupJob&amp;quot;,&lt;br /&gt;
  &amp;quot;nickname&amp;quot;: &amp;quot;testCreateMixedHostsAndParams&amp;quot;,&lt;br /&gt;
  &amp;quot;pickupBufferMinutes&amp;quot;: 5,&lt;br /&gt;
  &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
  &amp;quot;state&amp;quot;: &amp;quot;ENABLED&amp;quot;,&lt;br /&gt;
  &amp;quot;schedule&amp;quot;: &amp;quot;@daily&amp;quot;,&lt;br /&gt;
  &amp;quot;effectiveDate&amp;quot;: &amp;quot;2012-01-10T15:33:00-0800&amp;quot;,&lt;br /&gt;
  &amp;quot;endDate&amp;quot;: &amp;quot;2013-01-10T15:33:00-0800&amp;quot;,&lt;br /&gt;
  &amp;quot;hosts&amp;quot;: [&lt;br /&gt;
    &amp;quot;host1&amp;quot;,&lt;br /&gt;
    &amp;quot;host2&amp;quot;&lt;br /&gt;
  ],&lt;br /&gt;
  &amp;quot;minExecutionDuration&amp;quot;: &amp;quot;1s&amp;quot;,&lt;br /&gt;
  &amp;quot;maxExecutionDuration&amp;quot;: &amp;quot;5m&amp;quot;,&lt;br /&gt;
  &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job should be auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
  &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
  &amp;quot;autoRetryCount&amp;quot;: 0, // indicates number of auto-retry attempts to be made on failure&lt;br /&gt;
  &amp;quot;autoRetryInterval&amp;quot;: 0, // indicates auto-retry minimum interval in minutes&lt;br /&gt;
  &amp;quot;autoRetryIntervalExponent&amp;quot;: false, // indicates whether to exponentially increase the interval between auto retries&lt;br /&gt;
  &amp;quot;customCalendarId&amp;quot;: null,&lt;br /&gt;
  &amp;quot;parameters&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;level&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;STRING&amp;quot;,&lt;br /&gt;
      &amp;quot;value&amp;quot;: &amp;quot;ERROR&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;level&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;STRING&amp;quot;,&lt;br /&gt;
      &amp;quot;value&amp;quot;: &amp;quot;{{globalLevels}}&amp;quot; // Values may contain global parameter references&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| jobClass || Y || Fully qualified class name of the job. Max 255 chars.&lt;br /&gt;
|- &lt;br /&gt;
| nickname || Y || Unique nickname for the job. Max 255 chars.&lt;br /&gt;
|- &lt;br /&gt;
| pickupBufferMinutes || Y || Pickup buffer minutes. Integer greater than zero.&lt;br /&gt;
|- &lt;br /&gt;
| recoveryType || Y || Recovery type as defined in [[Unified_API#Enumerations|Enumerations]].&lt;br /&gt;
|- &lt;br /&gt;
| state || Y || Initial schedule&#039;s job status as defined in [[Unified_API#Enumerations|Enumerations]].&lt;br /&gt;
|- &lt;br /&gt;
| schedule || Y/N || If state is ENABLED, the mandatory cron-style schedule for the job. If not ENABLED, this should be omitted. You may specify multiple cron patterns delimiting them with a semi-colon.&lt;br /&gt;
|- &lt;br /&gt;
| effectiveDate || N || Optional effective date for the initial schedule, with no seconds specified. If not set, this defaults to next minute. Until this date is reached, the job is DISABLED.&lt;br /&gt;
|- &lt;br /&gt;
| endDate || N || Optional end date for the initial schedule, with no seconds specified. If set, the job will become DISABLED after this date passes.&lt;br /&gt;
|- &lt;br /&gt;
| customCalendarId || N || Optional custom calendar id.&lt;br /&gt;
|- &lt;br /&gt;
| hosts || N || Zero or more host names that this job may run on. If none set, the job may run on any host.&lt;br /&gt;
|- &lt;br /&gt;
| minExecutionDuration || N || The minimum expected job runtime. Format is an integer greater than zero immediately followed by &amp;quot;s&amp;quot;, &amp;quot;m&amp;quot; or &amp;quot;h&amp;quot;. Example: &amp;quot;15m&amp;quot;.&lt;br /&gt;
|- &lt;br /&gt;
| maxExecutionDuration || N ||  The maximum expected job runtime. Format is an integer greater than zero immediately followed by &amp;quot;s&amp;quot;, &amp;quot;m&amp;quot; or &amp;quot;h&amp;quot;. Example: &amp;quot;15m&amp;quot;.&lt;br /&gt;
|- &lt;br /&gt;
| autoInterrupt || N ||  Boolean indicating whether auto interrupt functionality is desired. May only be true when the job is an interruptable job and a maxExecutionDuration has been specified. Defaults to false.&lt;br /&gt;
|- &lt;br /&gt;
| autoRetryCount|| Y || Number of auto retries on non-interrupted execution failure. 0 if none are desired.&lt;br /&gt;
|- &lt;br /&gt;
| autoRetryInterval|| N || Minimum number of minutes between auto retries - calculated from failure time. Defaults to 0 and indicates try at next available opportunity.&lt;br /&gt;
|- &lt;br /&gt;
| autoRetryIntervalExponent|| N || Boolean indicating whether to exponentially increase interval time between retries.&lt;br /&gt;
|- &lt;br /&gt;
| chainAll || N || Boolean indicating whether all chained instances are triggered when job is currently running. Otherwise, only one newly chained record is created.&lt;br /&gt;
|- &lt;br /&gt;
| parameters || Y/N || Zero or more parameter definitions. If a job defines required parameters with the &amp;lt;code&amp;gt;@Configuration&amp;lt;/code&amp;gt; annotation, a job will fail to create unless they are supplied. Otherwise, this field is optional. Parameter definitions must have values for &amp;quot;name&amp;quot;, &amp;quot;type&amp;quot; and &amp;quot;value&amp;quot;, where type is a valid parameter type outlined in [[Unified_API#Enumerations|Enumerations]]. To define multiple values for a single parameter name, simply include multiple items in the parameters collection.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as GET.&lt;br /&gt;
&lt;br /&gt;
==PUT updates to an existing job==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;PUT http(s)://localhost/rest/jobs/{jobId}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobUpdateRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobDetail&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Updates a job&#039;s configuration. Does not support schedule changes or additions. For schedule changes, see [[#POST a new schedule to an existing job|POST a new schedule to an existing job]]. &lt;br /&gt;
&lt;br /&gt;
This endpoint will only update fields that are supplied in the request, similar to a PATCH request. You may update one or more fields as desired.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.LogCleanupJob&amp;quot;,&lt;br /&gt;
  &amp;quot;nickname&amp;quot;: &amp;quot;testCreateMixedHostsAndParams&amp;quot;,&lt;br /&gt;
  &amp;quot;pickupBufferMinutes&amp;quot;: 5,&lt;br /&gt;
  &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
  &amp;quot;hosts&amp;quot;: [&lt;br /&gt;
    &amp;quot;host1&amp;quot;,&lt;br /&gt;
    &amp;quot;host2&amp;quot;&lt;br /&gt;
  ],&lt;br /&gt;
  &amp;quot;minExecutionDuration&amp;quot;: &amp;quot;1s&amp;quot;,&lt;br /&gt;
  &amp;quot;maxExecutionDuration&amp;quot;: &amp;quot;5m&amp;quot;,&lt;br /&gt;
  &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
  &amp;quot;autoRetryCount&amp;quot;: 0, // indicates number of auto-retry attempts to be made on failure&lt;br /&gt;
  &amp;quot;autoRetryInterval&amp;quot;: 0, // indicates auto-retry minimum interval in minutes&lt;br /&gt;
  &amp;quot;autoRetryIntervalExponent&amp;quot;: false, // indicates whether to exponentially increase the interval between auto retries&lt;br /&gt;
  &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
  &amp;quot;parameters&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;level&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;STRING&amp;quot;,&lt;br /&gt;
      &amp;quot;value&amp;quot;: &amp;quot;ERROR&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;level&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;STRING&amp;quot;,&lt;br /&gt;
      &amp;quot;value&amp;quot;: &amp;quot;WARN&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTNable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| jobClass || N || Fully qualified class name of the job. Max 255 chars.&lt;br /&gt;
|- &lt;br /&gt;
| nickname || N || Unique nickname for the job. Max 50 chars.&lt;br /&gt;
|- &lt;br /&gt;
| pickupBufferMinutes || N || Pickup buffer minutes. Integer greater than zero.&lt;br /&gt;
|- &lt;br /&gt;
| recoveryType || N || Recovery type as defined in [[Unified_API#Enumerations|Enumerations]].&lt;br /&gt;
|- &lt;br /&gt;
| hosts || N || Zero or more host names that this job may run on. If none set, the job may run on any host.&lt;br /&gt;
|- &lt;br /&gt;
| minExecutionDuration || N || The minimum expected job runtime. Format is an integer greater than zero immediately followed by &amp;quot;s&amp;quot;, &amp;quot;m&amp;quot; or &amp;quot;h&amp;quot;. Example: &amp;quot;15m&amp;quot;.&lt;br /&gt;
|- &lt;br /&gt;
| maxExecutionDuration || N ||  The maximum expected job runtime. Format is an integer greater than zero immediately followed by &amp;quot;s&amp;quot;, &amp;quot;m&amp;quot; or &amp;quot;h&amp;quot;. Example: &amp;quot;2h&amp;quot;.&lt;br /&gt;
|- &lt;br /&gt;
| autoInterrupt || N ||  Boolean indicating whether auto interrupt functionality is desired. May only be true when the job is an interruptable job and a maxExecutionDuration has been specified. Defaults to false.&lt;br /&gt;
|- &lt;br /&gt;
| autoRetryCount || N || Number of auto retries on non-interrupted execution failure. 0 if none are desired.&lt;br /&gt;
|- &lt;br /&gt;
| autoRetryInterval|| N || Minimum number of minutes between auto retries - calculated from failure time. Defaults to 0 and indicates try at next available opportunity.&lt;br /&gt;
|- &lt;br /&gt;
| autoRetryIntervalExponent|| N || Boolean indicating whether to exponentially increase interval time between retries.&lt;br /&gt;
|- &lt;br /&gt;
| chainAll || N || Boolean indicating whether all chained instances are triggered when job is currently running. Otherwise, only one newly chained record is created.&lt;br /&gt;
|- &lt;br /&gt;
| parameters || N || Zero or more parameter definitions. If a job defines required parameters with the &amp;lt;code&amp;gt;@Configuration&amp;lt;/code&amp;gt; annotation, a job will fail to create unless they are supplied. Otherwise, this field is optional. Parameter definitions must have values for &amp;quot;name&amp;quot;, &amp;quot;type&amp;quot; and &amp;quot;value&amp;quot;, where type is a valid parameter type outlined in [[Unified_API#Enumerations|Enumerations]]. To define multiple values for a single parameter name, simply include multiple items in the parameters collection.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as GET.&lt;br /&gt;
&lt;br /&gt;
==DELETE an existing job==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;DELETE http(s)://localhost/rest/jobs/{jobId}[?cascade=true]&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobDetail&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Deletes a job and its history. Returns a 404 if not found.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Query String Parameters&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| cascade|| N || If set to true, all job conflict and chain definitions for this job will also be deleted. If not set, or set to false, any existing job conflicts or chain definitions will cause the request to fail.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as GET, and return the final state of the job before the delete.&lt;br /&gt;
&lt;br /&gt;
==GET a list of an existing job&#039;s schedules==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/jobs/{jobId}/schedules[?start=1356987599000&amp;amp;end=1357510546000]&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobScheduleListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns historical schedules for a job. This is essentially a subset of the primary GET endpoint for an existing job.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Query String Parameters&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| start || N || Start date for the runtimes to preview (inclusive).&lt;br /&gt;
|- &lt;br /&gt;
| end || N || End date for the runtimes to preview (inclusive). Must be after the start time.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;schedules&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;effectiveDate&amp;quot;: &amp;quot;2012-01-06T15:53:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;endDate&amp;quot;: &amp;quot;2012-08-06T15:53:00-0700&amp;quot;,&lt;br /&gt;
      &amp;quot;schedule&amp;quot;: &amp;quot;@hourly&amp;quot;,&lt;br /&gt;
      &amp;quot;scheduleDescription&amp;quot;: &amp;quot;On the hour&amp;quot;, (as of 5.2.0)&lt;br /&gt;
      &amp;quot;status&amp;quot;: &amp;quot;ENABLED&amp;quot;,&lt;br /&gt;
      &amp;quot;jobScheduleId&amp;quot;: 37,&lt;br /&gt;
      &amp;quot;customCalendarId&amp;quot;: 123 // if configured, the custom calendar&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;effectiveDate&amp;quot;: &amp;quot;2015-01-06T15:53:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;endDate&amp;quot;: &amp;quot;2016-01-06T15:53:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;status&amp;quot;: &amp;quot;AD_HOC_ACTIVE&amp;quot;,&lt;br /&gt;
      &amp;quot;jobScheduleId&amp;quot;: 38&lt;br /&gt;
    }&lt;br /&gt;
  ],&lt;br /&gt;
  &amp;quot;jobId&amp;quot;: 35,&lt;br /&gt;
  &amp;quot;currentJobScheduleId&amp;quot;: 38&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==POST a new schedule to an existing job==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;POST http(s)://localhost/rest/jobs/{jobId}/schedules&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.schedule.ScheduleCreationRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobScheduleListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Creates a new schedule for the job. This may be used to immediately change a job&#039;s scheduling state, or to schedule a future change. Creating a new schedule automatically splits and merges existing schedules. For example, if you have an enabled job and you disabled it for a day, the job will automatically re-enable after that day.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;state&amp;quot;: &amp;quot;ENABLED&amp;quot;,&lt;br /&gt;
  &amp;quot;schedule&amp;quot;: &amp;quot;@daily&amp;quot;,&lt;br /&gt;
  &amp;quot;effectiveDate&amp;quot;: &amp;quot;2012-01-10T15:33:00-0800&amp;quot;,&lt;br /&gt;
  &amp;quot;endDate&amp;quot;: &amp;quot;2013-01-10T15:33:00-0800&amp;quot;,&lt;br /&gt;
  &amp;quot;customCalendarId&amp;quot;: 123 // if desired, the custom calendar&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| state || Y || The schedule&#039;s job status as defined in [[Unified_API#Enumerations|Enumerations]].&lt;br /&gt;
|- &lt;br /&gt;
| schedule || Y/N || If state is ENABLED, the mandatory cron-style schedule for the job. If not ENABLED, this should be omitted. You may specify multiple cron patterns delimiting them with a semi-colon.&lt;br /&gt;
|- &lt;br /&gt;
| effectiveDate || N || Optional effective date for the schedule, with no seconds specified. If not set, this defaults to next minute. Until this date is reached, the job is DISABLED.&lt;br /&gt;
|- &lt;br /&gt;
| endDate || N || Optional end date for the schedule, with no seconds specified. If set, the job will become DISABLED after this date passes.&lt;br /&gt;
|- &lt;br /&gt;
| customCalendarId || N || Optional custom calendar for schedule.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as GET.&lt;br /&gt;
&lt;br /&gt;
==GET a list of configured global parameters ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/global_parameters&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.GlobalParameterListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Lists the configured global parameters&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;parameters&amp;quot;: [&lt;br /&gt;
      { &lt;br /&gt;
         &amp;quot;name&amp;quot;: &amp;quot;forceSSL&amp;quot;,&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;BOOLEAN&amp;quot;,&lt;br /&gt;
         &amp;quot;values&amp;quot;: [&amp;quot;false&amp;quot;]&lt;br /&gt;
      },&lt;br /&gt;
      { &lt;br /&gt;
         &amp;quot;name&amp;quot;: &amp;quot;hostNames&amp;quot;,&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;STRING&amp;quot;,&lt;br /&gt;
         &amp;quot;values&amp;quot;: [&amp;quot;example.com&amp;quot;, &amp;quot;test.com&amp;quot;]&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==PUT updates to global parameters ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;PUT http(s)://localhost/rest/global_parameters&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.GlobalParameterUpdateRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.GlobalParameterListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Replaces the configured global parameters with the supplied values. If the value for parameters is missing or empty, all global parameters will be deleted. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Calls to remove or alter global parameters may fail if jobs that use them do not pass parameter validation as a result of the change. This can be caused by removing a referenced global parameter or values that cannot be interpreted as the appropriate type in a job.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;parameters&amp;quot;: [&lt;br /&gt;
      { &lt;br /&gt;
         &amp;quot;name&amp;quot;: &amp;quot;forceSSL&amp;quot;,&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;BOOLEAN&amp;quot;,&lt;br /&gt;
         &amp;quot;values&amp;quot;: [&amp;quot;false&amp;quot;]&lt;br /&gt;
      },&lt;br /&gt;
      { &lt;br /&gt;
         &amp;quot;name&amp;quot;: &amp;quot;hostNames&amp;quot;,&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;STRING&amp;quot;,&lt;br /&gt;
         &amp;quot;values&amp;quot;: [&amp;quot;example.com&amp;quot;, &amp;quot;test.com&amp;quot;]&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| parameters || N || Zero or more parameter definitions. Parameter definitions must have values for &amp;quot;name&amp;quot;, &amp;quot;type&amp;quot; and &amp;quot;values&amp;quot;, where type is a valid parameter type outlined in Enumerations.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as GET.&lt;br /&gt;
&lt;br /&gt;
==GET a list of job folders ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/job_folders&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobFolderListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Available as of version 4.1.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Lists all used job folders in both flat and hierarchical modes.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;flat&amp;quot;: [&lt;br /&gt;
    &amp;quot;Prod&amp;quot;,&lt;br /&gt;
    &amp;quot;Prod/Test&amp;quot;,&lt;br /&gt;
    &amp;quot;QA&amp;quot;,&lt;br /&gt;
    &amp;quot;QA/123/456/789/Test&amp;quot;,&lt;br /&gt;
    &amp;quot;QA/Tester&amp;quot;&lt;br /&gt;
  ],&lt;br /&gt;
  &amp;quot;hierarchy&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;folder&amp;quot;: &amp;quot;Prod&amp;quot;,&lt;br /&gt;
      &amp;quot;children&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;folder&amp;quot;: &amp;quot;Test&amp;quot;,&lt;br /&gt;
          &amp;quot;children&amp;quot;: [&lt;br /&gt;
&lt;br /&gt;
          ],&lt;br /&gt;
          &amp;quot;jobUsingDirectly&amp;quot;: true&lt;br /&gt;
        }&lt;br /&gt;
      ],&lt;br /&gt;
      &amp;quot;jobUsingDirectly&amp;quot;: true&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;folder&amp;quot;: &amp;quot;QA&amp;quot;,&lt;br /&gt;
      &amp;quot;children&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;folder&amp;quot;: &amp;quot;123&amp;quot;,&lt;br /&gt;
          &amp;quot;children&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
              &amp;quot;folder&amp;quot;: &amp;quot;456&amp;quot;,&lt;br /&gt;
              &amp;quot;children&amp;quot;: [&lt;br /&gt;
                {&lt;br /&gt;
                  &amp;quot;folder&amp;quot;: &amp;quot;789&amp;quot;,&lt;br /&gt;
                  &amp;quot;children&amp;quot;: [&lt;br /&gt;
                    {&lt;br /&gt;
                      &amp;quot;folder&amp;quot;: &amp;quot;Test&amp;quot;,&lt;br /&gt;
                      &amp;quot;children&amp;quot;: [&lt;br /&gt;
&lt;br /&gt;
                      ],&lt;br /&gt;
                      &amp;quot;jobUsingDirectly&amp;quot;: true&lt;br /&gt;
                    }&lt;br /&gt;
                  ],&lt;br /&gt;
                  &amp;quot;jobUsingDirectly&amp;quot;: false&lt;br /&gt;
                }&lt;br /&gt;
              ],&lt;br /&gt;
              &amp;quot;jobUsingDirectly&amp;quot;: false&lt;br /&gt;
            }&lt;br /&gt;
          ],&lt;br /&gt;
          &amp;quot;jobUsingDirectly&amp;quot;: false&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;folder&amp;quot;: &amp;quot;Tester&amp;quot;,&lt;br /&gt;
          &amp;quot;children&amp;quot;: [&lt;br /&gt;
&lt;br /&gt;
          ],&lt;br /&gt;
          &amp;quot;jobUsingDirectly&amp;quot;: true&lt;br /&gt;
        }&lt;br /&gt;
      ],&lt;br /&gt;
      &amp;quot;jobUsingDirectly&amp;quot;: true&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Runtimes Endpoints (i.e. Job History) =&lt;br /&gt;
&lt;br /&gt;
==GET a list of scheduled runtimes (supports multiple jobs)==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/job_runtimes[?startKey=12345&amp;amp;status=RUNNING&amp;amp;host=host1&amp;amp;quantity=100&amp;amp;sort=asc]&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.history.RuntimeListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns a list of scheduled or completed job runtimes (i.e. history), optionally filtered by query string parameters. Ordering is guaranteed to be in order of scheduled time descending, unless overridden by the &amp;lt;code&amp;gt;sort&amp;lt;/code&amp;gt; parameter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt; field in the response indicates that there were too many results to return (i.e. exceeded &amp;lt;code&amp;gt;maxRecords&amp;lt;/code&amp;gt; as configured in the [[Admin Scheduler Settings|scheduler settings]] screen, or the &amp;lt;code&amp;gt;quantity&amp;lt;/code&amp;gt; parameter). To fetch the next page of results, invoke the same endpoint with the &amp;lt;code&amp;gt;startKey&amp;lt;/code&amp;gt; query string parameter set to the returned &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Query String Parameters&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| jobId || N || Restricts the search to the selected jobs. Supports multiple values.&lt;br /&gt;
|-&lt;br /&gt;
| status || N || Restricts the search to the selected statuses. See [[Unified_API#Enumerations|Enumerations]] for valid values. Supports multiple values.&lt;br /&gt;
|-&lt;br /&gt;
| host || N || If specified, only job runtimes that are assigned to the specified host(s) are included. Supports multiple values.&lt;br /&gt;
|-&lt;br /&gt;
| folder || N || If specified, only job runtimes with jobs matching the supplied folders are returned. If a parent path is supplied, all jobs containing that path or subpaths are included in the results. If an empty string is supplied, jobs with no folder will be returned. Supports multiple values. &#039;&#039;Available from version 4.1.0 forward.&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| start || N || Start date for the job runtimes to return (inclusive). Defaults to 24 hours ago. &lt;br /&gt;
|- &lt;br /&gt;
| end || N || End date for the job runtimes to return (inclusive). Defaults to a day after the start time. Must be after the start time.&lt;br /&gt;
|- &lt;br /&gt;
| startKey || N || If requesting the next page of results from a previous call, set it to the returned &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
|- &lt;br /&gt;
| quantity || N || Indicates the maximum number of results to return. This overrides the &amp;lt;code&amp;gt;maxRecords&amp;lt;/code&amp;gt; as configured in the [[Admin Scheduler Settings|scheduler settings]] screen.&lt;br /&gt;
|- &lt;br /&gt;
| sort || N || A value of either &amp;quot;asc&amp;quot; or &amp;quot;desc&amp;quot;, which controls the ordering of returned results. In all cases, the job scheduled time is used to sort results.&lt;br /&gt;
|-&lt;br /&gt;
| param_&#039;&#039;parameterName&#039;&#039; || N || If specified, values starting with &#039;&#039;param_&#039;&#039; can be used to match only on runtimes with specific runtime parameter values (not job-level parameters). If multiple values for the same query parameter starting with &#039;&#039;param_&#039;&#039; are supplied, a runtime is matched if any of its configured values match one of the supplied values. If &#039;&#039;param_&#039;&#039; filters with separate names are used, each must have a matching value for the runtime to be returned.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;nextPageStartKey&amp;quot; : &amp;quot;2013-01-06T16:05:00-0800 634&amp;quot;,&lt;br /&gt;
  &amp;quot;start&amp;quot;: &amp;quot;2013-10-31T23:59:00-0800&amp;quot;, // the inclusive search from date&lt;br /&gt;
  &amp;quot;end&amp;quot;: &amp;quot;2013-11-01T23:59:00-0800&amp;quot;, // the inclusive search to date&lt;br /&gt;
  &amp;quot;runtimes&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;runtimeOrdinal&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;heartbeatTime&amp;quot;: &amp;quot;2013-01-06T16:05:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;jobRuntimeId&amp;quot;: 8,&lt;br /&gt;
      &amp;quot;adHoc&amp;quot;: false,&lt;br /&gt;
      &amp;quot;pickupTime&amp;quot;: &amp;quot;2013-01-06T16:04:00-0800&amp;quot;,&lt;br /&gt;
      // present when this job was resubmitted from another job runtime&lt;br /&gt;
      &amp;quot;resubmissionSource&amp;quot;: {&lt;br /&gt;
        &amp;quot;jobRuntimeId&amp;quot;: 10,&lt;br /&gt;
        &amp;quot;status&amp;quot;: &amp;quot;READY&amp;quot;,&lt;br /&gt;
        &amp;quot;scheduledTime&amp;quot;: &amp;quot;2013-01-06T16:06:00-0800&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;endTime&amp;quot;: &amp;quot;2013-01-06T16:05:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;revision&amp;quot;: 1,&lt;br /&gt;
      &amp;quot;resubmission&amp;quot;: false,&lt;br /&gt;
      &amp;quot;scheduledTime&amp;quot;: &amp;quot;2013-01-06T16:04:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;autoRetryCount&amp;quot;: 2, // present if this job was auto-retried from a failure&lt;br /&gt;
      &amp;quot;autoRetryInterval&amp;quot;: 0, // indicates auto-retry minimum interval in minutes&lt;br /&gt;
      &amp;quot;autoRetryIntervalExponent&amp;quot;: false, // indicates whether to exponentially increase the interval between auto retries&lt;br /&gt;
      &amp;quot;status&amp;quot;: &amp;quot;FAILED&amp;quot;,&lt;br /&gt;
      &amp;quot;error&amp;quot;: { // present if the job fails&lt;br /&gt;
         &amp;quot;message&amp;quot;: &amp;quot;arg was null&amp;quot;,&lt;br /&gt;
         &amp;quot;detail&amp;quot;: &amp;quot;stack trace...&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;job&amp;quot;: {&lt;br /&gt;
        &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
        &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
        &amp;quot;jobId&amp;quot;: 43,&lt;br /&gt;
        &amp;quot;nickname&amp;quot;: &amp;quot;jobThatChainsOthers&amp;quot;,&lt;br /&gt;
        &amp;quot;interruptable&amp;quot;: false, // indicates if job can be interrupted&lt;br /&gt;
        &amp;quot;autoRetryCount&amp;quot;: 0, // indicates number of auto-retry attempts to be made on failure&lt;br /&gt;
        &amp;quot;autoRetryInterval&amp;quot;: 0, // indicates auto-retry minimum interval in minutes&lt;br /&gt;
        &amp;quot;autoRetryIntervalExponent&amp;quot;: false, // indicates whether to exponentially increase the interval between auto retries&lt;br /&gt;
        &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
        &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
        &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
        &amp;quot;revision&amp;quot; : 0,&lt;br /&gt;
        &amp;quot;activeSchedule&amp;quot;: {&lt;br /&gt;
          &amp;quot;jobScheduleId&amp;quot;: 44,&lt;br /&gt;
          &amp;quot;schedule&amp;quot;: &amp;quot;@hourly&amp;quot;,&lt;br /&gt;
          &amp;quot;scheduleDescription&amp;quot;: &amp;quot;On the hour&amp;quot;, (as of 5.2.0)&lt;br /&gt;
          &amp;quot;status&amp;quot;: &amp;quot;ENABLED&amp;quot;,&lt;br /&gt;
          &amp;quot;endDate&amp;quot;: &amp;quot;2013-01-06T17:44:00-0800&amp;quot;,&lt;br /&gt;
          &amp;quot;effectiveDate&amp;quot;: &amp;quot;2013-01-06T16:03:00-0800&amp;quot;,&lt;br /&gt;
          &amp;quot;customCalendarId&amp;quot;: 123 // if configured, the custom calendar&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;jobClass&amp;quot;: &amp;quot;someclass&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      // optional interruption element if job execution was interrupted&lt;br /&gt;
      &amp;quot;interruption&amp;quot; : {&lt;br /&gt;
          &amp;quot;requester&amp;quot;: &amp;quot;userName&amp;quot;,&lt;br /&gt;
          &amp;quot;requestTime&amp;quot;: &amp;quot;2013-01-06T16:04:54-0800&amp;quot;,&lt;br /&gt;
          &amp;quot;interruptTime&amp;quot;:&amp;quot;2013-01-06T16:04:56-0800&amp;quot; // this time will be set if successfully interrupted (otherwise not present)&lt;br /&gt;
      },&lt;br /&gt;
      // contains a list of job runtimes that were chained from this job runtime&lt;br /&gt;
      &amp;quot;chainTargets&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;trigger&amp;quot;: true,&lt;br /&gt;
          &amp;quot;detail&amp;quot;: null, // if trigger is false, this will contains details of why it didn&#039;t trigger&lt;br /&gt;
          &amp;quot;jobRuntimeId&amp;quot;: 9,&lt;br /&gt;
          &amp;quot;job&amp;quot;: {&lt;br /&gt;
            &amp;quot;jobId&amp;quot;: 44,&lt;br /&gt;
            &amp;quot;nickname&amp;quot;: &amp;quot;jobThatGetsChained&amp;quot;&lt;br /&gt;
          },&lt;br /&gt;
          &amp;quot;scheduledTime&amp;quot;: &amp;quot;2013-01-06T16:05:00-0800&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
      ],&lt;br /&gt;
      &amp;quot;executionType&amp;quot;: &amp;quot;Resubmission&amp;quot; // when present, indicates it executed as a &amp;quot;Resubmission&amp;quot;, &amp;quot;Chained&amp;quot;, or &amp;quot;Ad Hoc&amp;quot; job&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;runtimeOrdinal&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;jobRuntimeId&amp;quot;: 9,&lt;br /&gt;
      &amp;quot;adHoc&amp;quot;: false,&lt;br /&gt;
      &amp;quot;revision&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;resubmission&amp;quot;: false,&lt;br /&gt;
      &amp;quot;scheduledTime&amp;quot;: &amp;quot;2013-01-06T16:05:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;status&amp;quot;: &amp;quot;READY&amp;quot;,&lt;br /&gt;
      &amp;quot;job&amp;quot;: {&lt;br /&gt;
        &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
        &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
        &amp;quot;jobId&amp;quot;: 44,&lt;br /&gt;
        &amp;quot;nickname&amp;quot;: &amp;quot;jobThatGetsChained&amp;quot;,&lt;br /&gt;
        &amp;quot;autoRetryCount&amp;quot;: 0, // indicates number of auto-retry attempts to be made on failure&lt;br /&gt;
        &amp;quot;autoRetryInterval&amp;quot;: 0, // indicates auto-retry minimum interval in minutes&lt;br /&gt;
        &amp;quot;autoRetryIntervalExponent&amp;quot;: false, // indicates whether to exponentially increase the interval between auto retries&lt;br /&gt;
        &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
        &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
        &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
        &amp;quot;revision&amp;quot; : 0,&lt;br /&gt;
        &amp;quot;activeSchedule&amp;quot;: {&lt;br /&gt;
          &amp;quot;jobScheduleId&amp;quot;: 45,&lt;br /&gt;
          &amp;quot;schedule&amp;quot;: &amp;quot;@hourly&amp;quot;,&lt;br /&gt;
          &amp;quot;scheduleDescription&amp;quot;: &amp;quot;On the hour&amp;quot;, (as of 5.2.0)&lt;br /&gt;
          &amp;quot;status&amp;quot;: &amp;quot;ENABLED&amp;quot;,&lt;br /&gt;
          &amp;quot;endDate&amp;quot;: &amp;quot;2013-01-06T17:44:00-0800&amp;quot;,&lt;br /&gt;
          &amp;quot;effectiveDate&amp;quot;: &amp;quot;2013-01-06T16:03:00-0800&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;jobClass&amp;quot;: &amp;quot;someclass2&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      // when present, this includes this runtime was chained as a result of another job runtime&lt;br /&gt;
      &amp;quot;chainSource&amp;quot;: {&lt;br /&gt;
        &amp;quot;trigger&amp;quot;: true,&lt;br /&gt;
        &amp;quot;detail&amp;quot;: &amp;quot;chained it&amp;quot;,&lt;br /&gt;
        &amp;quot;jobRuntimeId&amp;quot;: 8,&lt;br /&gt;
        &amp;quot;job&amp;quot;: {&lt;br /&gt;
          &amp;quot;jobId&amp;quot;: 43,&lt;br /&gt;
          &amp;quot;nickname&amp;quot;: &amp;quot;jobThatChainsOthers&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;scheduledTime&amp;quot;: &amp;quot;2013-01-06T16:04:00-0800&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;chainTargets&amp;quot;: [ ],&lt;br /&gt;
      &amp;quot;executionType&amp;quot;: &amp;quot;Chained&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GET a list of a job&#039;s scheduled runtimes==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/jobs/{jobId}/runtimes&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.history.RuntimeListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This endpoint is equivalent the other runtime endpoint (see preceding item) with a URL like the following: &amp;lt;code&amp;gt;GET http(s)://localhost/rest/job_runtimes?jobId={jobId}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other than the jobId, all other query string parameters from the multi-job endpoint are supported.&lt;br /&gt;
&lt;br /&gt;
==POST a new scheduled runtime for an existing job (i.e. submit a one-time or ad hoc run)==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;POST http(s)://localhost/rest/jobs/{jobId}/runtimes&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.history.RuntimeSubmissionRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.history.RuntimeSubmissionResult&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Allows for submission of an ad hoc job run (executed immediately), or a one-time run scheduled for a later time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The job must be in a valid state to allow for execution (i.e. &amp;lt;code&amp;gt;UNSCHEDULED_ACTIVE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ENABLED&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;AD_HOC_ACTIVE&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;scheduledTime&amp;quot;: &amp;quot;2013-02-06T16:40:00-0800&amp;quot;,&lt;br /&gt;
  // Optional. Parameters supplied for scheduled runtime which will be available to the job when executing.&lt;br /&gt;
 &amp;quot;parameters&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;level&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;STRING&amp;quot;,&lt;br /&gt;
      &amp;quot;value&amp;quot;: &amp;quot;ERROR&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;level&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;STRING&amp;quot;,&lt;br /&gt;
      &amp;quot;value&amp;quot;: &amp;quot;WARN&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| scheduledTime|| N || The scheduled time, when the request is for a scheduled one-time run. If not supplied, the runtime is submitted for immediate execution as an ad hoc job.&lt;br /&gt;
|- &lt;br /&gt;
| parameters || N || Zero or more parameter definitions. Parameter definitions must have values for &amp;quot;name&amp;quot;, &amp;quot;type&amp;quot; and &amp;quot;value&amp;quot;, where type is a valid parameter type outlined in [[Unified_API#Enumerations|Enumerations]]. To define multiple values for a single parameter name, simply include multiple items in the parameters collection. If the parameter name matches a parameter defined for the job, it must be of the same type, and it will completely replace all configured values at the job level. &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; A &amp;lt;code&amp;gt;jobRuntimeId&amp;lt;/code&amp;gt; is only returned in the case of an ad hoc run.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments) &#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;jobRuntimeId&amp;quot;: 2, // only returned for ad hoc submission (no scheduled time supplied)&lt;br /&gt;
  &amp;quot;jobId&amp;quot;: 36,&lt;br /&gt;
  &amp;quot;scheduledTime&amp;quot;: &amp;quot;2013-01-06T16:37:00-0800&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GET details of an existing scheduled (or completed) job runtime==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/job_runtimes/{jobRuntimeId}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.history.RuntimeResult&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns detailed information for the requested job runtime. Responses will contain all the same details as a single record from a  &amp;lt;code&amp;gt;/job_runtimes&amp;lt;/code&amp;gt; GET request, with the addition of the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;parameters&amp;lt;/code&amp;gt; elements, which contain saved job results and runtime-specific parameters respectively.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;runtime&amp;quot;: {&lt;br /&gt;
    &amp;quot;heartbeatTime&amp;quot;: &amp;quot;2013-01-06T16:27:00-0800&amp;quot;,&lt;br /&gt;
    &amp;quot;jobRuntimeId&amp;quot;: 2,&lt;br /&gt;
    &amp;quot;adHoc&amp;quot;: false,&lt;br /&gt;
    &amp;quot;pickupTime&amp;quot;: &amp;quot;2013-01-06T16:26:00-0800&amp;quot;,&lt;br /&gt;
    &amp;quot;endTime&amp;quot;: &amp;quot;2013-01-06T16:27:00-0800&amp;quot;,&lt;br /&gt;
    &amp;quot;revision&amp;quot;: 6,&lt;br /&gt;
    &amp;quot;resubmission&amp;quot;: false,&lt;br /&gt;
    &amp;quot;scheduledTime&amp;quot;: &amp;quot;2013-01-06T16:26:00-0800&amp;quot;,&lt;br /&gt;
    &amp;quot;runningHost&amp;quot;: &amp;quot;test3&amp;quot;,&lt;br /&gt;
    &amp;quot;status&amp;quot;: &amp;quot;FAILED&amp;quot;,&lt;br /&gt;
    &amp;quot;interruptable&amp;quot;: false, // indicates if job can be interrupted&lt;br /&gt;
    &amp;quot;autoRetryCount&amp;quot;: 2, // present if this job was auto-retried from a failure&lt;br /&gt;
    &amp;quot;autoRetryInterval&amp;quot;: 0, // indicates auto-retry minimum interval in minutes&lt;br /&gt;
    &amp;quot;autoRetryIntervalExponent&amp;quot;: false, // indicates whether to exponentially increase the interval between auto retries&lt;br /&gt;
    &amp;quot;job&amp;quot;: {&lt;br /&gt;
      &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
      &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
      &amp;quot;jobId&amp;quot;: 36,&lt;br /&gt;
      &amp;quot;nickname&amp;quot;: &amp;quot;testWithOutput&amp;quot;,&lt;br /&gt;
      &amp;quot;activeSchedule&amp;quot;: {&lt;br /&gt;
        &amp;quot;jobScheduleId&amp;quot;: 37,&lt;br /&gt;
        &amp;quot;schedule&amp;quot;: &amp;quot;@hourly&amp;quot;,&lt;br /&gt;
        &amp;quot;scheduleDescription&amp;quot;: &amp;quot;On the hour&amp;quot;, (as of 5.2.0)&lt;br /&gt;
        &amp;quot;status&amp;quot;: &amp;quot;ENABLED&amp;quot;,&lt;br /&gt;
        &amp;quot;endDate&amp;quot;: &amp;quot;2013-01-06T18:06:00-0800&amp;quot;,&lt;br /&gt;
        &amp;quot;effectiveDate&amp;quot;: &amp;quot;2013-01-06T16:25:00-0800&amp;quot;,&lt;br /&gt;
        &amp;quot;customCalendarId&amp;quot;: 123 // if configured, the custom calendar for this job&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;jobClass&amp;quot;: &amp;quot;someclass&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;chainSource&amp;quot;: null,&lt;br /&gt;
    &amp;quot;chainTargets&amp;quot;: [&lt;br /&gt;
      &lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;output&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;jobRuntimeResultId&amp;quot;: 4,&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;testname&amp;quot;,&lt;br /&gt;
        &amp;quot;value&amp;quot;: &amp;quot;testvalue&amp;quot;,&lt;br /&gt;
        &amp;quot;valueType&amp;quot;: &amp;quot;java.lang.String&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;jobRuntimeResultId&amp;quot;: 5,&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;testname&amp;quot;,&lt;br /&gt;
        &amp;quot;value&amp;quot;: &amp;quot;testvalue2&amp;quot;,&lt;br /&gt;
        &amp;quot;valueType&amp;quot;: &amp;quot;java.lang.String&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;jobRuntimeResultId&amp;quot;: 6,&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;testname2&amp;quot;,&lt;br /&gt;
        &amp;quot;value&amp;quot;: &amp;quot;testvalue3&amp;quot;,&lt;br /&gt;
        &amp;quot;valueType&amp;quot;: &amp;quot;java.lang.String&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
    // Parameters specified for ad-hoc or one-time runtime. This does not include parameters defined at the job level.&lt;br /&gt;
    &amp;quot;parameters&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;level&amp;quot;,&lt;br /&gt;
        &amp;quot;type&amp;quot;: &amp;quot;STRING&amp;quot;, // see Enumerations above for valid values&lt;br /&gt;
        &amp;quot;values&amp;quot;: [ &amp;quot;WARN&amp;quot;, &amp;quot;ERROR&amp;quot; ]&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;maxAgeDays&amp;quot;,&lt;br /&gt;
        &amp;quot;type&amp;quot;: &amp;quot;INTEGER&amp;quot;,&lt;br /&gt;
        &amp;quot;values&amp;quot;: [ &amp;quot;60&amp;quot; ] // values is always a list for consistency&lt;br /&gt;
      }&lt;br /&gt;
    ]&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==DELETE a future scheduled runtime ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Since Obsidian 4.7.0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;DELETE http(s)://localhost/rest/jobs/{jobId}/runtimes&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.history.OneTimeRunDeleteRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.history.OneTimeDeletionResult&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Allows for deletion of a future scheduled ad hoc job run.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; When more than on runtime is scheduled for the given time, this function will remove gaps, that is ensure the remaining ordinals start at 0 and increment without skipping any values. For example, if you have 3 instances scheduled (ordinals 0, 1 &amp;amp; 2) and request ordinal 0 be deleted, the remaining two ordinals (1 &amp;amp; 2) will be renumbered to 0 &amp;amp; 1.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;scheduledTime&amp;quot;: &amp;quot;2021-02-01T19:00:00-0800&amp;quot;,&lt;br /&gt;
  // Optional if only one instance scheduled at the specified time.&lt;br /&gt;
 &amp;quot;runtimeOrdinal&amp;quot;: 2&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| scheduledTime|| Y || The scheduled time, when the job was previously requested to be run. Must yet be in the future.&lt;br /&gt;
|- &lt;br /&gt;
| runtimeOrdinal|| Y/N || The ordinal of future dated runtime. If only one runtime is scheduled for the given date, the value can be omitted or should be 0. If more than one instance is scheduled at the given time, the actual ordinal must be specified. &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments) &#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;jobId&amp;quot;: 2,&lt;br /&gt;
  &amp;quot;jobRuntimeId&amp;quot;: 36389,&lt;br /&gt;
  &amp;quot;scheduledTime&amp;quot;: &amp;quot;2021-02-01T19:00:00-0800&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==POST a resubmission request for a failed job runtime==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;POST http(s)://localhost/rest/job_runtimes/{jobRuntimeId}/resubmissions&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.history.RuntimeResubmissionResult&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Allows for resubmission of a failed job runtime.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response &#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;resubmission&amp;quot;: {&lt;br /&gt;
    &amp;quot;revision&amp;quot;: 0,&lt;br /&gt;
    &amp;quot;jobId&amp;quot;: 35,&lt;br /&gt;
    &amp;quot;resubmission&amp;quot;: true,&lt;br /&gt;
    &amp;quot;runtimeOrdinal&amp;quot;: 0,&lt;br /&gt;
    &amp;quot;scheduledTime&amp;quot;: &amp;quot;2013-01-06T16:49:00-0800&amp;quot;,&lt;br /&gt;
    &amp;quot;status&amp;quot;: &amp;quot;READY&amp;quot;,&lt;br /&gt;
    &amp;quot;jobRuntimeId&amp;quot;: 2&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==POST an interruption request to kill a running job ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;POST http(s)://localhost/rest/job_runtimes/{jobRuntimeId}/interrupts&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.history.JobInterruptResult&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Allows for interruption of a currently running job runtime. This request can only be made once successfully.&lt;br /&gt;
&lt;br /&gt;
An interruption request will result in the job being terminated, as long as it does not terminate naturally very soon after the request is made, and it is capable of shutting down. Not all jobs can be terminated. See [[Implementing_Jobs#Interruptable_Jobs|Interruptable Jobs]] for full details.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response &#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;interruption&amp;quot;: {&lt;br /&gt;
    &amp;quot;requester&amp;quot;: &amp;quot;apiUserName&amp;quot;,&lt;br /&gt;
    &amp;quot;requestTime&amp;quot;: &amp;quot;2013-01-06T16:49:00-0800&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== POST async results ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;POST http(s)://localhost/rest/job_runtimes/{jobRuntimeId}/async_results&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.history.AsyncJobRuntimeResultsRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.history.RuntimeResult&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Available as of version 4.5.0.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Used to indicate the final status of an Async job. This request can only be made once successfully. See [[Implementing_Jobs#Async_Jobs | Async Job]] for full details.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request &#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;asyncJobRuntimeStatus&amp;quot;: &amp;quot;FAILED&amp;quot;,&lt;br /&gt;
	&amp;quot;resultTime&amp;quot;: &amp;quot;2018-03-09T22:04:08-0500&amp;quot;,&lt;br /&gt;
	&amp;quot;jobResults&amp;quot;: {&lt;br /&gt;
			&amp;quot;Job Failure Description&amp;quot;: &amp;quot;Could not acquire locks on all resources. Tables [reference_object,reference_entity] were not optimized.&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;resultException&amp;quot;: {&amp;quot;detailMessage&amp;quot;:&amp;quot;Lock Not Acquired&amp;quot;,&amp;quot;stackTrace&amp;quot;:[{&amp;quot;declaringClass&amp;quot;:&amp;quot;com.carfey.finance.OptimizeDatabase&amp;quot;,&amp;quot;methodName&amp;quot;:&amp;quot;optimize&amp;quot;,&amp;quot;fileName&amp;quot;:&amp;quot;OptimizeDatabase.java&amp;quot;,&amp;quot;lineNumber&amp;quot;:132},{&amp;quot;declaringClass&amp;quot;:&amp;quot;com.carfey.ops.job.OptimizeDatabase&amp;quot;,&amp;quot;methodName&amp;quot;:&amp;quot;acquireLocks&amp;quot;,&amp;quot;fileName&amp;quot;:&amp;quot;OptimizeDatabase.java&amp;quot;,&amp;quot;lineNumber&amp;quot;:445}],&amp;quot;suppressedExceptions&amp;quot;:[]}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response &#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;runtime&amp;quot;: {&lt;br /&gt;
        &amp;quot;runningHost&amp;quot;: &amp;quot;obsidian-production&amp;quot;,&lt;br /&gt;
        &amp;quot;runtimeOrdinal&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;output&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
                &amp;quot;name&amp;quot;: &amp;quot;Job Failure Description&amp;quot;,&lt;br /&gt;
                &amp;quot;value&amp;quot;: &amp;quot;Could not acquire locks on all resources. Tables [reference_object,reference_entity] were not optimized.&amp;quot;,&lt;br /&gt;
                &amp;quot;valueType&amp;quot;: &amp;quot;java.lang.String&amp;quot;,&lt;br /&gt;
                &amp;quot;jobRuntimeResultId&amp;quot;: 551&lt;br /&gt;
            }&lt;br /&gt;
        ],&lt;br /&gt;
        &amp;quot;pickupTime&amp;quot;: &amp;quot;2018-03-09T07:45:30-0500&amp;quot;,&lt;br /&gt;
        &amp;quot;adHoc&amp;quot;: false,&lt;br /&gt;
        &amp;quot;heartbeatTime&amp;quot;: &amp;quot;2018-03-09T07:46:59-0500&amp;quot;,&lt;br /&gt;
        &amp;quot;lastUpdatedBy&amp;quot;: &amp;quot;REST: webServiceCallback&amp;quot;,&lt;br /&gt;
        &amp;quot;scheduledTime&amp;quot;: &amp;quot;2018-03-09T07:45:00-0500&amp;quot;,&lt;br /&gt;
        &amp;quot;revision&amp;quot;: 603,&lt;br /&gt;
        &amp;quot;resubmission&amp;quot;: false,&lt;br /&gt;
        &amp;quot;job&amp;quot;: {&lt;br /&gt;
            &amp;quot;hostPreference&amp;quot;: false,&lt;br /&gt;
            &amp;quot;autoInterrupt&amp;quot;: false,&lt;br /&gt;
            &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
            &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
            &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.WebServiceJob&amp;quot;,&lt;br /&gt;
            &amp;quot;nickname&amp;quot;: &amp;quot;Finance DB Optimization Service&amp;quot;,&lt;br /&gt;
            &amp;quot;lastUpdatedBy&amp;quot;: &amp;quot;devops&amp;quot;,&lt;br /&gt;
            &amp;quot;activeSchedule&amp;quot;: {&lt;br /&gt;
                &amp;quot;schedule&amp;quot;: &amp;quot;@daily&amp;quot;,&lt;br /&gt;
                &amp;quot;lastUpdatedBy&amp;quot;: &amp;quot;devops&amp;quot;,&lt;br /&gt;
                &amp;quot;lastUpdatedDate&amp;quot;: &amp;quot;2018-03-08T23:57:37-0500&amp;quot;,&lt;br /&gt;
                &amp;quot;createdDate&amp;quot;: &amp;quot;2018-03-08T23:57:37-0500&amp;quot;,&lt;br /&gt;
                &amp;quot;endDate&amp;quot;: &amp;quot;2999-12-31T23:59:00-0500&amp;quot;,&lt;br /&gt;
                &amp;quot;createdBy&amp;quot;: &amp;quot;devops&amp;quot;,&lt;br /&gt;
                &amp;quot;jobScheduleId&amp;quot;: 152,&lt;br /&gt;
                &amp;quot;effectiveDate&amp;quot;: &amp;quot;2018-03-08T23:58:00-0500&amp;quot;,&lt;br /&gt;
                &amp;quot;status&amp;quot;: &amp;quot;ENABLED&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;interruptable&amp;quot;: false,&lt;br /&gt;
            &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;revision&amp;quot;: 201,&lt;br /&gt;
            &amp;quot;jobId&amp;quot;: 1,&lt;br /&gt;
            &amp;quot;lastUpdatedDate&amp;quot;: &amp;quot;2018-03-08T23:57:37-0500&amp;quot;,&lt;br /&gt;
            &amp;quot;createdDate&amp;quot;: &amp;quot;2018-03-08T14:47:33-0500&amp;quot;,&lt;br /&gt;
            &amp;quot;createdBy&amp;quot;: &amp;quot;devops&amp;quot;,&lt;br /&gt;
            &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
            &amp;quot;chainAll&amp;quot;: false&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;parameters&amp;quot;: [],&lt;br /&gt;
        &amp;quot;status&amp;quot;: &amp;quot;FAILED&amp;quot;,&lt;br /&gt;
        &amp;quot;chainTargets&amp;quot;: [],&lt;br /&gt;
        &amp;quot;error&amp;quot;: {&lt;br /&gt;
            &amp;quot;exceptionClass&amp;quot;: &amp;quot;java.lang.Exception&amp;quot;,&lt;br /&gt;
            &amp;quot;detail&amp;quot;: &amp;quot;java.lang.Exception: Lock Not Acquired\r\n\tat com.carfey.finance.OptimizeDatabase.optimize(OptimizeDatabase.java:132)\r\n\tat com.carfey.ops.job.OptimizeDatabase.acquireLocks(OptimizeDatabase.java:445)\r\n&amp;quot;,&lt;br /&gt;
            &amp;quot;message&amp;quot;: &amp;quot;Lock Not Acquired&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;jobRuntimeId&amp;quot;: 756,&lt;br /&gt;
        &amp;quot;lastUpdatedDate&amp;quot;: &amp;quot;2018-03-09T21:50:57-0500&amp;quot;,&lt;br /&gt;
        &amp;quot;createdDate&amp;quot;: &amp;quot;2018-03-09T07:44:01-0500&amp;quot;,&lt;br /&gt;
        &amp;quot;createdBy&amp;quot;: &amp;quot;JobQueuer&amp;quot;,&lt;br /&gt;
        &amp;quot;endTime&amp;quot;: &amp;quot;2018-03-09T22:04:08-0500&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GET a list of the latest scheduled runtime by job (supports multiple jobs)==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/job_runtimes/latest[?startKey=12345&amp;amp;host=host1&amp;amp;quantity=100&amp;amp;sort=asc]&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.history.JobDashboardListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Available as of version 4.10.2.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Returns a list of the latest job runtimes by job(i.e. history), optionally filtered by query string parameters. Ordering is guaranteed to be in order of scheduled time descending, unless overridden by the &amp;lt;code&amp;gt;sort&amp;lt;/code&amp;gt; parameter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt; field in the response indicates that there were too many results to return (i.e. exceeded &amp;lt;code&amp;gt;maxRecords&amp;lt;/code&amp;gt; as configured in the [[Admin Scheduler Settings|scheduler settings]] screen, or the &amp;lt;code&amp;gt;quantity&amp;lt;/code&amp;gt; parameter). To fetch the next page of results, invoke the same endpoint with the &amp;lt;code&amp;gt;startKey&amp;lt;/code&amp;gt; query string parameter set to the returned &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Query String Parameters&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| jobId || N || Restricts the search to the selected jobs. Supports multiple values. &#039;&#039;Do not combine with nickname.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| nickname || N || If specified, only jobs matching the supplied nickname are returned. Wildcards may be included to support partial matches by using %, or exact literals can be used. For example, to find all jobs containing the word &amp;quot;order&amp;quot;, use &amp;quot;%order%&amp;quot;. Supports multiple values. &#039;&#039;Do not combine with jobId.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| host || N || If specified, only the latest job runtimes that are assigned to the specified host(s) are included. Supports multiple values.&lt;br /&gt;
|-&lt;br /&gt;
| folder || N || If specified, only job runtimes with jobs matching the supplied folders are returned. If a parent path is supplied, all jobs containing that path or subpaths are included in the results. If an empty string is supplied, jobs with no folder will be returned. Supports multiple values.&lt;br /&gt;
|- &lt;br /&gt;
| startKey || N || If requesting the next page of results from a previous call, set it to the returned &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
|- &lt;br /&gt;
| quantity || N || Indicates the maximum number of results to return. This overrides the &amp;lt;code&amp;gt;maxRecords&amp;lt;/code&amp;gt; as configured in the [[Admin Scheduler Settings|scheduler settings]] screen.&lt;br /&gt;
|- &lt;br /&gt;
| sort || N || A value of either &amp;quot;asc&amp;quot; or &amp;quot;desc&amp;quot;, which controls the ordering of returned results. In all cases, the job scheduled time is used to sort results.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;nextPageStartKey&amp;quot; : &amp;quot;2013-01-06T16:05:00-0800 634&amp;quot;,&lt;br /&gt;
  &amp;quot;start&amp;quot;: &amp;quot;2013-10-31T23:59:00-0800&amp;quot;,&lt;br /&gt;
  &amp;quot;end&amp;quot;: &amp;quot;2013-11-01T23:59:00-0800&amp;quot;,&lt;br /&gt;
  &amp;quot;runtimes&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;runtimeOrdinal&amp;quot;: 0, &lt;br /&gt;
      &amp;quot;heartbeatTime&amp;quot;: &amp;quot;2013-01-06T16:05:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;jobRuntimeId&amp;quot;: 8,&lt;br /&gt;
      &amp;quot;adHoc&amp;quot;: false,&lt;br /&gt;
      &amp;quot;pickupTime&amp;quot;: &amp;quot;2013-01-06T16:04:00-0800&amp;quot;,&lt;br /&gt;
      // present when this job was resubmitted from another job runtime&lt;br /&gt;
      &amp;quot;resubmissionSource&amp;quot;: {&lt;br /&gt;
        &amp;quot;jobRuntimeId&amp;quot;: 10,&lt;br /&gt;
        &amp;quot;status&amp;quot;: &amp;quot;READY&amp;quot;,&lt;br /&gt;
        &amp;quot;scheduledTime&amp;quot;: &amp;quot;2013-01-06T16:06:00-0800&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;endTime&amp;quot;: &amp;quot;2013-01-06T16:05:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;revision&amp;quot;: 1,&lt;br /&gt;
      &amp;quot;resubmission&amp;quot;: false,&lt;br /&gt;
      &amp;quot;scheduledTime&amp;quot;: &amp;quot;2013-01-06T16:04:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;autoRetryCount&amp;quot;: 2, // present if this job was auto-retried from a failure &lt;br /&gt;
      &amp;quot;autoRetryInterval&amp;quot;: 0, // indicates auto-retry minimum interval in minutes&lt;br /&gt;
      &amp;quot;autoRetryIntervalExponent&amp;quot;: false, // indicates whether to exponentially increase the interval between auto retries&lt;br /&gt;
      &amp;quot;status&amp;quot;: &amp;quot;FAILED&amp;quot;,&lt;br /&gt;
      &amp;quot;error&amp;quot;: { // present if the job fails&lt;br /&gt;
         &amp;quot;message&amp;quot;: &amp;quot;arg was null&amp;quot;,&lt;br /&gt;
         &amp;quot;detail&amp;quot;: &amp;quot;stack trace...&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;job&amp;quot;: {&lt;br /&gt;
        &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
        &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
        &amp;quot;jobId&amp;quot;: 43,&lt;br /&gt;
        &amp;quot;nickname&amp;quot;: &amp;quot;jobThatChainsOthers&amp;quot;,&lt;br /&gt;
        &amp;quot;interruptable&amp;quot;: false, // indicates if job can be interrupted&lt;br /&gt;
        &amp;quot;autoRetryCount&amp;quot;: 0, // indicates number of auto-retry attempts to be made on failure&lt;br /&gt;
        &amp;quot;autoRetryInterval&amp;quot;: 0, // indicates auto-retry minimum interval in minutes&lt;br /&gt;
        &amp;quot;autoRetryIntervalExponent&amp;quot;: false, // indicates whether to exponentially increase the interval between auto retries&lt;br /&gt;
        &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
        &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
        &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
        &amp;quot;revision&amp;quot; : 0,&lt;br /&gt;
        &amp;quot;activeSchedule&amp;quot;: {&lt;br /&gt;
          &amp;quot;jobScheduleId&amp;quot;: 44,&lt;br /&gt;
          &amp;quot;schedule&amp;quot;: &amp;quot;@hourly&amp;quot;,&lt;br /&gt;
          &amp;quot;scheduleDescription&amp;quot;: &amp;quot;On the hour&amp;quot;, (as of 5.2.0)&lt;br /&gt;
          &amp;quot;status&amp;quot;: &amp;quot;ENABLED&amp;quot;,&lt;br /&gt;
          &amp;quot;endDate&amp;quot;: &amp;quot;2013-01-06T17:44:00-0800&amp;quot;,&lt;br /&gt;
          &amp;quot;effectiveDate&amp;quot;: &amp;quot;2013-01-06T16:03:00-0800&amp;quot;,&lt;br /&gt;
          &amp;quot;customCalendarId&amp;quot;: 123 // if configured, the custom calendar&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;jobClass&amp;quot;: &amp;quot;someclass&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      // optional interruption element if job execution was interrupted&lt;br /&gt;
      &amp;quot;interruption&amp;quot; : {&lt;br /&gt;
          &amp;quot;requester&amp;quot;: &amp;quot;userName&amp;quot;,&lt;br /&gt;
          &amp;quot;requestTime&amp;quot;: &amp;quot;2013-01-06T16:04:54-0800&amp;quot;,&lt;br /&gt;
          &amp;quot;interruptTime&amp;quot;:&amp;quot;2013-01-06T16:04:56-0800&amp;quot; // this time will be set if successfully interrupted (otherwise not present)&lt;br /&gt;
      },&lt;br /&gt;
      // contains a list of job runtimes that were chained from this job runtime&lt;br /&gt;
      &amp;quot;chainTargets&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;trigger&amp;quot;: true,&lt;br /&gt;
          &amp;quot;detail&amp;quot;: null, // if trigger is false, this will contains details of why it didn&#039;t trigger&lt;br /&gt;
          &amp;quot;jobRuntimeId&amp;quot;: 9,&lt;br /&gt;
          &amp;quot;job&amp;quot;: {&lt;br /&gt;
            &amp;quot;jobId&amp;quot;: 44,&lt;br /&gt;
            &amp;quot;nickname&amp;quot;: &amp;quot;jobThatGetsChained&amp;quot;&lt;br /&gt;
          },&lt;br /&gt;
          &amp;quot;scheduledTime&amp;quot;: &amp;quot;2013-01-06T16:05:00-0800&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
      ],&lt;br /&gt;
      &amp;quot;executionType&amp;quot;: &amp;quot;Resubmission&amp;quot; // when present, indicates it executed as a &amp;quot;Resubmission&amp;quot;, &amp;quot;Chained&amp;quot;, or &amp;quot;Ad Hoc&amp;quot; job&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;runtimeOrdinal&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;jobRuntimeId&amp;quot;: 9,&lt;br /&gt;
      &amp;quot;adHoc&amp;quot;: false,&lt;br /&gt;
      &amp;quot;revision&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;resubmission&amp;quot;: false,&lt;br /&gt;
      &amp;quot;scheduledTime&amp;quot;: &amp;quot;2013-01-06T16:05:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;status&amp;quot;: &amp;quot;READY&amp;quot;,&lt;br /&gt;
      &amp;quot;job&amp;quot;: {&lt;br /&gt;
        &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
        &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
        &amp;quot;jobId&amp;quot;: 44,&lt;br /&gt;
        &amp;quot;nickname&amp;quot;: &amp;quot;jobThatGetsChained&amp;quot;,&lt;br /&gt;
        &amp;quot;autoRetryCount&amp;quot;: 0, // indicates number of auto-retry attempts to be made on failure&lt;br /&gt;
        &amp;quot;autoRetryInterval&amp;quot;: 0, // indicates auto-retry minimum interval in minutes&lt;br /&gt;
        &amp;quot;autoRetryIntervalExponent&amp;quot;: false, // indicates whether to exponentially increase the interval between auto retries&lt;br /&gt;
        &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
        &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
        &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
        &amp;quot;revision&amp;quot; : 0,&lt;br /&gt;
        &amp;quot;activeSchedule&amp;quot;: {&lt;br /&gt;
          &amp;quot;jobScheduleId&amp;quot;: 45,&lt;br /&gt;
          &amp;quot;schedule&amp;quot;: &amp;quot;@hourly&amp;quot;,&lt;br /&gt;
          &amp;quot;scheduleDescription&amp;quot;: &amp;quot;On the hour&amp;quot;, (as of 5.2.0)&lt;br /&gt;
          &amp;quot;status&amp;quot;: &amp;quot;ENABLED&amp;quot;,&lt;br /&gt;
          &amp;quot;endDate&amp;quot;: &amp;quot;2013-01-06T17:44:00-0800&amp;quot;,&lt;br /&gt;
          &amp;quot;effectiveDate&amp;quot;: &amp;quot;2013-01-06T16:03:00-0800&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;jobClass&amp;quot;: &amp;quot;someclass2&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      // when present, this includes this runtime was chained as a result of another job runtime&lt;br /&gt;
      &amp;quot;chainSource&amp;quot;: {&lt;br /&gt;
        &amp;quot;trigger&amp;quot;: true,&lt;br /&gt;
        &amp;quot;detail&amp;quot;: &amp;quot;chained it&amp;quot;,&lt;br /&gt;
        &amp;quot;jobRuntimeId&amp;quot;: 8,&lt;br /&gt;
        &amp;quot;job&amp;quot;: {&lt;br /&gt;
          &amp;quot;jobId&amp;quot;: 43,&lt;br /&gt;
          &amp;quot;nickname&amp;quot;: &amp;quot;jobThatChainsOthers&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;scheduledTime&amp;quot;: &amp;quot;2013-01-06T16:04:00-0800&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;chainTargets&amp;quot;: [ ],&lt;br /&gt;
      &amp;quot;executionType&amp;quot;: &amp;quot;Chained&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Runtime Preview Endpoints =&lt;br /&gt;
&lt;br /&gt;
==GET a list of runtime previews (supports multiple jobs)==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/job_runtimes/previews[?jobId=1&amp;amp;jobId=2&amp;amp;start=1356987599000&amp;amp;end=1357510546000]&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.schedule.RuntimePreviewListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns a preview of runtimes, optionally filtered based on the supplied query string parameters. This is useful to see when jobs will run during a given time period. Note that these are an estimate of runtimes and cannot account for overlapped jobs, schedule changes or other issues that may result in altered execution times. Results are ordered by scheduled time ascending.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The capped field in the response indicates that there were too many results to return (i.e. exceeded &amp;lt;code&amp;gt;maxRecords&amp;lt;/code&amp;gt; as configured in the [[Admin Scheduler Settings|scheduler settings]] screen). If you are hitting this condition, try limiting your date range or other parameters.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Query String Parameters&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| jobId || N || Restricts the preview to the selected jobs. Supports multiple values.&lt;br /&gt;
|- &lt;br /&gt;
| start || N || Start date for the runtimes to preview (inclusive).  Defaults to the current minute.&lt;br /&gt;
|- &lt;br /&gt;
| end || N || End date for the runtimes to preview (inclusive). Defaults to a day after the start time. Must be after the start time.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;capped&amp;quot;: false,&lt;br /&gt;
  &amp;quot;start&amp;quot;: &amp;quot;2013-10-31T23:59:00-0800&amp;quot;, // the inclusive search from date&lt;br /&gt;
  &amp;quot;end&amp;quot;: &amp;quot;2013-11-01T23:59:00-0800&amp;quot;, // the inclusive search to date&lt;br /&gt;
  &amp;quot;runtimes&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;jobId&amp;quot;: 36,&lt;br /&gt;
      &amp;quot;nickname&amp;quot;: &amp;quot;jobOne&amp;quot;,&lt;br /&gt;
      &amp;quot;schedule&amp;quot;: &amp;quot;* * * * *&amp;quot;,&lt;br /&gt;
      &amp;quot;scheduleDescription&amp;quot;: &amp;quot;Every minute every day&amp;quot;, (as of 5.2.0)&lt;br /&gt;
      &amp;quot;scheduledTime&amp;quot;: &amp;quot;2013-01-06T15:31:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;scheduleEffectiveDate&amp;quot;: &amp;quot;2013-01-06T13:50:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;scheduleEndDate&amp;quot;: &amp;quot;2999-12-31T23:59:00-0800&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;jobId&amp;quot;: 37,&lt;br /&gt;
      &amp;quot;nickname&amp;quot;: &amp;quot;jobTwo&amp;quot;,&lt;br /&gt;
      &amp;quot;schedule&amp;quot;: &amp;quot;* * * * *&amp;quot;,&lt;br /&gt;
      &amp;quot;scheduleDescription&amp;quot;: &amp;quot;Every minute every day&amp;quot;, (as of 5.2.0)&lt;br /&gt;
      &amp;quot;scheduledTime&amp;quot;: &amp;quot;2013-01-06T15:30:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;scheduleEffectiveDate&amp;quot;: &amp;quot;2013-01-06T13:50:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;scheduleEndDate&amp;quot;: &amp;quot;2999-12-31T23:59:00-0800&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GET a list of runtime previews for an existing job==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/jobs/{jobId}/runtimes/previews&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.schedule.RuntimePreviewListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This endpoint is equivalent the other runtime preview endpoint (see preceding item) with a URL like the following:&lt;br /&gt;
&amp;lt;code&amp;gt;GET http(s)://localhost/rest/job_runtimes/previews?jobId={jobId}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other than the &amp;lt;code&amp;gt;jobId&amp;lt;/code&amp;gt;, all other query string parameters from the multi-job endpoint are supported.&lt;br /&gt;
&lt;br /&gt;
= Job Chain Endpoints =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==GET a list of job chains==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/chains[?active=true&amp;amp;sourceJobId=123&amp;amp;targetJobId=456]&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobChainListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns a list of configured job chains, optionally filtered by query string parameters.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Query String Parameters&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|-&lt;br /&gt;
| active || N || Limits results to those matching the active flag (true/false).&lt;br /&gt;
|-&lt;br /&gt;
| sourceJobId || N || Limits results to those matching the supplied source job ID.&lt;br /&gt;
|-&lt;br /&gt;
| targetJobId || N || Limits results to those matching the supplied target job ID.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;jobChains&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;jobChainId&amp;quot;: 1,&lt;br /&gt;
      &amp;quot;schedule&amp;quot;: &amp;quot;* * * * *&amp;quot;, // if specified&lt;br /&gt;
      &amp;quot;scheduleDescription&amp;quot;: &amp;quot;Every minute every day&amp;quot;, (as of 5.2.0)&lt;br /&gt;
      &amp;quot;active&amp;quot;: true,&lt;br /&gt;
      &amp;quot;sourceJob&amp;quot;: {&lt;br /&gt;
         &amp;quot;jobId&amp;quot;: 51,&lt;br /&gt;
         &amp;quot;recoveryType&amp;quot;: &amp;quot;LAST&amp;quot;,&lt;br /&gt;
         &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;nickname&amp;quot;: &amp;quot;Order Export Job&amp;quot;,&lt;br /&gt;
         &amp;quot;folder&amp;quot;: &amp;quot;Production/Test&amp;quot;, // as of 4.1.0&lt;br /&gt;
         &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
         &amp;quot;jobClass&amp;quot;: &amp;quot;com.example.OrderExportJob&amp;quot;,&lt;br /&gt;
         &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
         &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
         &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
         &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
         &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
         &amp;quot;revision&amp;quot;: 2&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;target&amp;quot;: {&lt;br /&gt;
         &amp;quot;jobId&amp;quot;: 52,&lt;br /&gt;
         &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
         &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;nickname&amp;quot;: &amp;quot;Archive Job&amp;quot;,&lt;br /&gt;
         &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
         &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.FileArchiveJob&amp;quot;,&lt;br /&gt;
         &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
         &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
         &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
         &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
         &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
         &amp;quot;revision&amp;quot;: 5&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;triggerStates&amp;quot;: [&amp;quot;FAILED&amp;quot;,&amp;quot;CONDITIONAL&amp;quot;],&lt;br /&gt;
      &amp;quot;resultConditions&amp;quot;: [ &lt;br /&gt;
        {&lt;br /&gt;
           &amp;quot;variableName&amp;quot;: &amp;quot;exportFile&amp;quot;,&lt;br /&gt;
           &amp;quot;operator&amp;quot;: &amp;quot;EXISTS&amp;quot;,&lt;br /&gt;
           &amp;quot;values&amp;quot;: [] // empty list when values do not apply&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
           &amp;quot;variableName&amp;quot;:&amp;quot;fileSize&amp;quot;,&lt;br /&gt;
           &amp;quot;operator&amp;quot;: &amp;quot;GREATER_THAN&amp;quot;,&lt;br /&gt;
           &amp;quot;values&amp;quot;: [&amp;quot;0&amp;quot;] // list with one element when multiples aren&#039;t applicable&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
           &amp;quot;variableName&amp;quot;: &amp;quot;status&amp;quot;,&lt;br /&gt;
           &amp;quot;operator&amp;quot;: &amp;quot;IN&amp;quot;,&lt;br /&gt;
           &amp;quot;values&amp;quot;: [&amp;quot;EXPORTED&amp;quot;, &amp;quot;ZIPPED&amp;quot;]&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;jobChainId&amp;quot;: 2,&lt;br /&gt;
      &amp;quot;active&amp;quot;: false,&lt;br /&gt;
      &amp;quot;sourceJob&amp;quot;: {&lt;br /&gt;
         &amp;quot;jobId&amp;quot;: 51,&lt;br /&gt;
         &amp;quot;recoveryType&amp;quot;: &amp;quot;LAST&amp;quot;,&lt;br /&gt;
         &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;nickname&amp;quot;: &amp;quot;Customer Export Job&amp;quot;,&lt;br /&gt;
         &amp;quot;folder&amp;quot;: &amp;quot;Production/Test&amp;quot;, // as of 4.1.0&lt;br /&gt;
         &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
         &amp;quot;jobClass&amp;quot;: &amp;quot;com.example.CustomerExportJob&amp;quot;,&lt;br /&gt;
         &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
         &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
         &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
         &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
         &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
         &amp;quot;revision&amp;quot;: 2&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;target&amp;quot;: {&lt;br /&gt;
         &amp;quot;jobId&amp;quot;: 52,&lt;br /&gt;
         &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
         &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;nickname&amp;quot;: &amp;quot;Archive Job&amp;quot;,&lt;br /&gt;
         &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
         &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.FileArchiveJob&amp;quot;,&lt;br /&gt;
         &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
         &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
         &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
         &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
         &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
         &amp;quot;revision&amp;quot;: 5&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;triggerStates&amp;quot;: [&amp;quot;COMPLETED&amp;quot;],&lt;br /&gt;
      &amp;quot;resultConditions&amp;quot;: [] // only populated when CONDITIONAL state is used&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GET details of an existing job chain==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/chains/{jobChainId}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobChain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns details of a configured job chain, or a 404 if not found. Contains the same set of fields as the job chain listing endpoint. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;jobChainId&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;schedule&amp;quot;: &amp;quot;* * * * *&amp;quot;, // if specified&lt;br /&gt;
   &amp;quot;scheduleDescription&amp;quot;: &amp;quot;Every minute every day&amp;quot;, (as of 5.2.0)&lt;br /&gt;
   &amp;quot;active&amp;quot;: true,&lt;br /&gt;
   &amp;quot;sourceJob&amp;quot;: {&lt;br /&gt;
      &amp;quot;jobId&amp;quot;: 51,&lt;br /&gt;
      &amp;quot;recoveryType&amp;quot;: &amp;quot;LAST&amp;quot;,&lt;br /&gt;
      &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
      &amp;quot;nickname&amp;quot;: &amp;quot;Order Export Job&amp;quot;,&lt;br /&gt;
      &amp;quot;folder&amp;quot;: &amp;quot;Production/Test&amp;quot;, // as of 4.1.0&lt;br /&gt;
      &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
      &amp;quot;jobClass&amp;quot;: &amp;quot;com.example.OrderExportJob&amp;quot;,&lt;br /&gt;
      &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
      &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
      &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
      &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
      &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
      &amp;quot;revision&amp;quot;: 2&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;target&amp;quot;: {&lt;br /&gt;
      &amp;quot;jobId&amp;quot;: 52,&lt;br /&gt;
      &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
      &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
      &amp;quot;nickname&amp;quot;: &amp;quot;Archive Job&amp;quot;,&lt;br /&gt;
      &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
      &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.FileArchiveJob&amp;quot;,&lt;br /&gt;
      &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
      &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
      &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
      &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
      &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
      &amp;quot;revision&amp;quot;: 5&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;triggerStates&amp;quot;: [&amp;quot;FAILED&amp;quot;, &amp;quot;CONDITIONAL&amp;quot;],&lt;br /&gt;
   &amp;quot;resultConditions&amp;quot;: [ // only populated when CONDITIONAL state is used&lt;br /&gt;
     {&lt;br /&gt;
        &amp;quot;variableName&amp;quot;: &amp;quot;exportFile&amp;quot;,&lt;br /&gt;
        &amp;quot;operator&amp;quot;: &amp;quot;EXISTS&amp;quot;,&lt;br /&gt;
        &amp;quot;values&amp;quot;: [] // empty list when values do not apply&lt;br /&gt;
     },&lt;br /&gt;
     {&lt;br /&gt;
        &amp;quot;variableName&amp;quot;: &amp;quot;fileSize&amp;quot;,&lt;br /&gt;
        &amp;quot;operator&amp;quot;: &amp;quot;GREATER_THAN&amp;quot;,&lt;br /&gt;
        &amp;quot;values&amp;quot;: [&amp;quot;0&amp;quot;] // list with one element when multiples aren&#039;t applicable&lt;br /&gt;
     },&lt;br /&gt;
     {&lt;br /&gt;
        &amp;quot;variableName&amp;quot;: &amp;quot;status&amp;quot;,&lt;br /&gt;
        &amp;quot;operator&amp;quot;: &amp;quot;IN&amp;quot;,&lt;br /&gt;
        &amp;quot;values&amp;quot;: [&amp;quot;EXPORTED&amp;quot;, &amp;quot;ZIPPED&amp;quot;]&lt;br /&gt;
     }&lt;br /&gt;
   ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==POST a new job chain==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;POST http(s)://localhost/rest/chains&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobChainUpdateRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobChain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Creates a new job chain.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;sourceJobId&amp;quot;: 12,&lt;br /&gt;
  &amp;quot;targetJobId&amp;quot;: 54,&lt;br /&gt;
  &amp;quot;schedule&amp;quot;: &amp;quot;* * * * *&amp;quot;, //optional&lt;br /&gt;
  &amp;quot;active&amp;quot;: true,&lt;br /&gt;
  &amp;quot;triggerStates&amp;quot;: [&amp;quot;CONDITIONAL&amp;quot;, &amp;quot;FAILED&amp;quot;],&lt;br /&gt;
  &amp;quot;resultConditions&amp;quot;: [ // only supplied when CONDITIONAL state is supplied&lt;br /&gt;
     {&lt;br /&gt;
        &amp;quot;variableName&amp;quot;: &amp;quot;exportFile&amp;quot;,&lt;br /&gt;
        &amp;quot;operator&amp;quot;:&amp;quot;EXISTS&amp;quot; // no &amp;quot;values&amp;quot; field required for EXISTS or NOT_EXISTS&lt;br /&gt;
     },&lt;br /&gt;
     {&lt;br /&gt;
        &amp;quot;variableName&amp;quot;: &amp;quot;fileSize&amp;quot;,&lt;br /&gt;
        &amp;quot;operator&amp;quot;: &amp;quot;GREATER_THAN&amp;quot;,&lt;br /&gt;
        &amp;quot;values&amp;quot;:[&amp;quot;0&amp;quot;] // in this case, only one value allowed&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| sourceJobId || Y || ID of the source job.&lt;br /&gt;
|- &lt;br /&gt;
| targetJobId || Y || ID of the target job to chain&lt;br /&gt;
|- &lt;br /&gt;
| schedule || N || Optional schedule that constrains when the job chain triggers.&lt;br /&gt;
|- &lt;br /&gt;
| active || Y || Flag to indicate whether the chain is active or not.&lt;br /&gt;
|- &lt;br /&gt;
| triggerStates || Y || One or more job chain states as defined in [[Unified_API#Enumerations|Enumerations]]. Note that &amp;lt;code&amp;gt;CONDITIONAL&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;COMPLETED&amp;lt;/code&amp;gt; types cannot be used on the same chain.&lt;br /&gt;
|- &lt;br /&gt;
| resultConditions || Y/N || Conditions based on job results that apply to the &amp;lt;code&amp;gt;CONDITIONAL&amp;lt;/code&amp;gt; trigger state. Must be supplied only when that state is used, in which case at least one condition must be supplied. Result conditions consist of a &amp;lt;code&amp;gt;variableName&amp;lt;/code&amp;gt;, an &amp;lt;code&amp;gt;operator&amp;lt;/code&amp;gt; as defined in [[Unified_API#Enumerations|Enumerations]], and for most operators, a list of &amp;lt;code&amp;gt;values&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;EXISTS&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;NOT EXISTS&amp;lt;/code&amp;gt; operators do not use values, so they must not be supplied. Operators &amp;lt;code&amp;gt;IN&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;NOT IN&amp;lt;/code&amp;gt; support one or more values, and all other operators accept a single value in the &amp;lt;code&amp;gt;values&amp;lt;/code&amp;gt; list. Values for this field map to bean class &amp;lt;code&amp;gt;com.carfey.ops.api.enums.JobChainConditionOperator&amp;lt;/code&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as GET.&lt;br /&gt;
&lt;br /&gt;
==PUT updates to an existing job chain==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;PUT http(s)://localhost/rest/chains/{jobChainId}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobChainUpdateRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobChain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Updates an existing job chain. Returns a 404 if not found.&lt;br /&gt;
&lt;br /&gt;
Requests and responses have the same format as POST.&lt;br /&gt;
&lt;br /&gt;
==DELETE an existing job chain==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;DELETE http(s)://localhost/rest/chains/{jobChainId}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobChain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Deletes a job chain. Returns a 404 if not found.&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as GET, and return the final state of the job chain before the delete.&lt;br /&gt;
&lt;br /&gt;
= Job Conflict Endpoints =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==GET a list of job conflicts ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/conflicts&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.ConflictListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Lists the configured job conflicts. Non-conflicted jobs are also included in the return value.&lt;br /&gt;
&lt;br /&gt;
Conflicting jobs are returned in priority order within the same list. Multiple conflicting job sets can be returned in the &amp;lt;code&amp;gt;conflictJobs&amp;lt;/code&amp;gt; field.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;conflictJobs&amp;quot;: [&lt;br /&gt;
    [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;jobId&amp;quot;: 51,&lt;br /&gt;
         &amp;quot;recoveryType&amp;quot;: &amp;quot;LAST&amp;quot;,&lt;br /&gt;
         &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;nickname&amp;quot;: &amp;quot;Archive Conflict Set A - 1&amp;quot;,&lt;br /&gt;
         &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
         &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.FileArchiveJob&amp;quot;,&lt;br /&gt;
         &amp;quot;folder&amp;quot;: &amp;quot;Production/Test&amp;quot;, // as of 4.1.0&lt;br /&gt;
         &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
         &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
         &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
         &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
         &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
         &amp;quot;revision&amp;quot;: 2&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;jobId&amp;quot;: 52,&lt;br /&gt;
         &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
         &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;nickname&amp;quot;: &amp;quot;Archive Conflict Set A - 2&amp;quot;,&lt;br /&gt;
         &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
         &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.FileArchiveJob&amp;quot;,&lt;br /&gt;
         &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
         &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
         &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
         &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
         &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
         &amp;quot;revision&amp;quot;: 5&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
    [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;jobId&amp;quot;: 53,&lt;br /&gt;
         &amp;quot;recoveryType&amp;quot;: &amp;quot;LAST&amp;quot;,&lt;br /&gt;
         &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;nickname&amp;quot;: &amp;quot;Archive Conflict Set B - 1&amp;quot;,&lt;br /&gt;
         &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
         &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.FileArchiveJob&amp;quot;,&lt;br /&gt;
         &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
         &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
         &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
         &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
         &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
         &amp;quot;revision&amp;quot;: 2&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;jobId&amp;quot;: 54,&lt;br /&gt;
         &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
         &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;nickname&amp;quot;: &amp;quot;Archive Conflict Set B - 2&amp;quot;,&lt;br /&gt;
         &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
         &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.FileArchiveJob&amp;quot;,&lt;br /&gt;
         &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
         &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
         &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
         &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
         &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
         &amp;quot;revision&amp;quot;: 5&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;jobId&amp;quot;: 55,&lt;br /&gt;
         &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
         &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;nickname&amp;quot;: &amp;quot;Archive Conflict Set B - 3&amp;quot;,&lt;br /&gt;
         &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
         &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.FileArchiveJob&amp;quot;,&lt;br /&gt;
         &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
         &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
         &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
         &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
         &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
         &amp;quot;revision&amp;quot;: 5&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
  ],&lt;br /&gt;
  &amp;quot;nonConflictJobs&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
         &amp;quot;jobId&amp;quot;: 56,&lt;br /&gt;
         &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
         &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;nickname&amp;quot;: &amp;quot;Non-Conflicted Job&amp;quot;,&lt;br /&gt;
         &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
         &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.FileArchiveJob&amp;quot;,&lt;br /&gt;
         &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
         &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
         &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
         &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
         &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
         &amp;quot;revision&amp;quot;: 5&lt;br /&gt;
    }&lt;br /&gt;
  ]   &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==PUT updates to job conflicts ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;PUT http(s)://localhost/rest/conflicts&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.ConflictUpdateRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobDetail&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Replaces the current job conflict configuration with the supplied configuration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{  &lt;br /&gt;
&lt;br /&gt;
  // Note that if Job 5 and Job 2 are scheduled for the same minute, Job 5 will run first as it is selected as the highest priority job from the first conflict set.&lt;br /&gt;
  // When priority is significant and varies across different sets, ensure your conflict sets are in the desired order.  &lt;br /&gt;
  &amp;quot;conflicts&amp;quot;: [&lt;br /&gt;
      [1, 2, 3, 5], // Job 1 has highest priority&lt;br /&gt;
      [2, 5, 4]     // Job 2 also conflicts with 4 &amp;amp; 5.&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| conflicts || N || A list of lists containing job IDs. Each inner list contains jobs that conflict with each other, in order of execution precedence. Jobs that do not conflict with any other jobs are simply omitted from this list. A job can exist in multiple conflict sets, but should only occur in a particular set once. To remove all job conflicts, an empty list can be supplied for this field.&lt;br /&gt;
&lt;br /&gt;
When selecting available non-conflicted jobs to run, Obsidian inspects the conflict sets in the order provided when they are saved, and selects the highest priority available job before moving onto the next conflict set. When priority is significant and varies across different sets, ensure your conflict sets are in the desired order.  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as GET.&lt;br /&gt;
&lt;br /&gt;
==GET a list of conflicts for a specific job==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/jobs/{jobId}/conflicts&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.job.JobConflictListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lists all jobs that conflict with the requested job.&lt;br /&gt;
&lt;br /&gt;
Conflicting jobs are returned in priority order, including the job for which this request was made, in order that its priority within the set can be determined. Note that if the job has no conflicts, the returned conflicting jobs list will be empty, and will not contain the requested job.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;conflictJobs&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;jobId&amp;quot;: 51,&lt;br /&gt;
         &amp;quot;recoveryType&amp;quot;: &amp;quot;LAST&amp;quot;,&lt;br /&gt;
         &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;nickname&amp;quot;: &amp;quot;Conflict A&amp;quot;,&lt;br /&gt;
         &amp;quot;folder&amp;quot;: &amp;quot;Production/Test&amp;quot;, // as of 4.1.0&lt;br /&gt;
         &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
         &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.FileArchiveJob&amp;quot;,&lt;br /&gt;
         &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
         &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
         &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
         &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
         &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
         &amp;quot;revision&amp;quot;: 2&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;jobId&amp;quot;: 52,&lt;br /&gt;
         &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
         &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;nickname&amp;quot;: &amp;quot;Conflict B&amp;quot;,&lt;br /&gt;
         &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
         &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.FileArchiveJob&amp;quot;,&lt;br /&gt;
         &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
         &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
         &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
         &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
         &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
         &amp;quot;revision&amp;quot;: 5&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;jobId&amp;quot;: 56, // this is the requested job&lt;br /&gt;
         &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
         &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;nickname&amp;quot;: &amp;quot;Requested Job&amp;quot;,&lt;br /&gt;
         &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
         &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.FileArchiveJob&amp;quot;,&lt;br /&gt;
         &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
         &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
         &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
         &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
         &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
         &amp;quot;revision&amp;quot;: 5&lt;br /&gt;
    } &lt;br /&gt;
  ],&lt;br /&gt;
  &amp;quot;job&amp;quot;: {&lt;br /&gt;
         &amp;quot;jobId&amp;quot;: 56,&lt;br /&gt;
         &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
         &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;nickname&amp;quot;: &amp;quot;Requested Job&amp;quot;,&lt;br /&gt;
         &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
         &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.FileArchiveJob&amp;quot;,&lt;br /&gt;
         &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
         &amp;quot;minExecutionDuration&amp;quot; : &amp;quot;1s&amp;quot;, // only present when defined on job&lt;br /&gt;
         &amp;quot;maxExecutionDuration&amp;quot; : &amp;quot;10m&amp;quot; // only present when defined on job&lt;br /&gt;
         &amp;quot;autoInterrupt&amp;quot; : false, //indicates if a job is auto interrupted when maxExecutionDuration is exceeded&lt;br /&gt;
         &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
         &amp;quot;revision&amp;quot;: 5&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Scheduling Hosts Endpoints =&lt;br /&gt;
&lt;br /&gt;
==GET a list of known scheduling hosts==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/hosts&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.host.HostListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns a list of known hosts. These are either running or recently shut down abnormally. Hosts that shut down normally are unregistered on shutdown. Note that returned IDs are transient and may change after startup or shutdown or a node. Heartbeat time indicates when the server last performed the heartbeat health check against the database.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;hosts&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 32,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;production1&amp;quot;,&lt;br /&gt;
      &amp;quot;heartbeatTime&amp;quot;: &amp;quot;2013-01-05T21:15:59-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;enabled&amp;quot;: true&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 33,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;production2&amp;quot;,&lt;br /&gt;
      &amp;quot;heartbeatTime&amp;quot;: &amp;quot;2013-01-05T21:15:59-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;enabled&amp;quot;: false&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GET details on an existing scheduling host==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/hosts/{id}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Alternate (by host name): &#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/hosts/names/{name}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.host.HostDetail&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns the requested host, or a 404 if not found.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The name field corresponds to the host name, as described [[Getting_Started#Setting_Host_Names|here]]. Explicit host names should be set if you intend to rely known host names in this endpoint. Heartbeat time indicates when the server last performed the heartbeat health check against the database.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;host&amp;quot;: {&lt;br /&gt;
    &amp;quot;id&amp;quot;: 33,&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;production1&amp;quot;,&lt;br /&gt;
    &amp;quot;heartbeatTime&amp;quot;: &amp;quot;2013-01-05T21:18:18-0800&amp;quot;,&lt;br /&gt;
    &amp;quot;licenceLeaseExpiryUtc&amp;quot;: &amp;quot;2017-06-15 01:36:06&amp;quot;,&lt;br /&gt;
    &amp;quot;enabled&amp;quot;: true,&lt;br /&gt;
    &amp;quot;licenceStatus&amp;quot;: &amp;quot;internet_node_verified&amp;quot;,&lt;br /&gt;
    &amp;quot;licenceLeaseExpiryUtc&amp;quot;: &amp;quot;2017-06-15 02:15:42&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GET health details on an existing scheduling host==&lt;br /&gt;
&#039;&#039;As of Obsidian 6.3.0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;GET http(s)://localhost/rest/hosts/{id}/health&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternate (by host name): &#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/hosts/names/{name}/health&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.host.HostDetail&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The intended use of this endpoint is as a health check. This endpoint response is to be parsed to evaluate health as it will include full health details with 200 response code if all is well, and a 400 if any part of system is unhealthy including license verification, job queuer and/or job spawner.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The name field corresponds to the host name, as described [[Getting_Started#Setting_Host_Names|here]]. Explicit host names should be set if you intend to rely known host names in this endpoint. Heartbeat time indicates when the server last performed the heartbeat health check against the database.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;host&amp;quot;: {&lt;br /&gt;
        &amp;quot;id&amp;quot;: 243,&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;obsidian.production&amp;quot;,&lt;br /&gt;
        &amp;quot;heartbeatTime&amp;quot;: &amp;quot;2025-07-17T20:28:34Z&amp;quot;,&lt;br /&gt;
        &amp;quot;licenceLeaseExpiryUtc&amp;quot;: &amp;quot;2025-07-18 01:27:14&amp;quot;,&lt;br /&gt;
        &amp;quot;enabled&amp;quot;: true,&lt;br /&gt;
        &amp;quot;licenceStatus&amp;quot;: &amp;quot;internet_node_verified&amp;quot;,&lt;br /&gt;
        &amp;quot;jobQueuerStatus&amp;quot;: &amp;quot;healthy&amp;quot;,//or laggy,delayed,unresponsive&lt;br /&gt;
        &amp;quot;jobQueuerUpdateTimeUtc&amp;quot;: &amp;quot;2025-07-17 20:28:28&amp;quot;,&lt;br /&gt;
        &amp;quot;jobSpawnerStatus&amp;quot;: &amp;quot;healthy&amp;quot;//or laggy,delayed,unresponsive&lt;br /&gt;
        &amp;quot;jobSpawnerUpdateTimeUtc&amp;quot;: &amp;quot;2025-07-17 20:28:20&amp;quot;,&lt;br /&gt;
        &amp;quot;createdDate&amp;quot;: &amp;quot;2025-07-17T19:27:13Z&amp;quot;,&lt;br /&gt;
        &amp;quot;createdBy&amp;quot;: &amp;quot;SchedulerStarter.startUp&amp;quot;,&lt;br /&gt;
        &amp;quot;lastUpdatedBy&amp;quot;: &amp;quot;SchedulerStarter.designator&amp;quot;,&lt;br /&gt;
        &amp;quot;lastUpdatedDate&amp;quot;: &amp;quot;2025-07-17T20:28:34Z&amp;quot;,&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GET licence details on an existing scheduling host==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/hosts/{id}/licence_health&amp;lt;/code&amp;gt;&#039;&#039;&#039;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;Prior to 4.5&#039;&#039;: &#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/hosts/licenceHealthCheck/{id}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Alternate (by host name): &#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/hosts/names/{name}/licence_health&amp;lt;/code&amp;gt;&#039;&#039;&#039;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;Prior to 4.5&#039;&#039;: &#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/hosts/licenceHealthCheck/names/{name}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.host.HostDetail&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The intended use of this endpoint is as a health check. This endpoint is identical to the [[REST_Endpoints#GET_details_on_an_existing_scheduling_host |GET details on an existing scheduling host]] above, with the exception that it will return a 400 if the licence has expired or is in some way invalid.&lt;br /&gt;
&lt;br /&gt;
Returns the requested host with licence status, or a 404 if not found, or a 400 if the licence is invalid or its lease has expired.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The name field corresponds to the host name, as described [[Getting_Started#Setting_Host_Names|here]]. Explicit host names should be set if you intend to rely known host names in this endpoint. Heartbeat time indicates when the server last performed the heartbeat health check against the database.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;host&amp;quot;: {&lt;br /&gt;
    &amp;quot;id&amp;quot;: 33,&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;production1&amp;quot;,&lt;br /&gt;
    &amp;quot;heartbeatTime&amp;quot;: &amp;quot;2013-01-05T21:18:18-0800&amp;quot;,&lt;br /&gt;
    &amp;quot;licenceLeaseExpiryUtc&amp;quot;: &amp;quot;2017-06-15 01:36:06&amp;quot;,&lt;br /&gt;
    &amp;quot;enabled&amp;quot;: true,&lt;br /&gt;
    &amp;quot;licenceStatus&amp;quot;: &amp;quot;internet_node_verified&amp;quot;,&lt;br /&gt;
    &amp;quot;licenceLeaseExpiryUtc&amp;quot;: &amp;quot;2017-06-15 02:15:42&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==PUT updates to an existing scheduling host==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;PUT http(s)://localhost/rest/hosts/{id}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Alternate (by host name): &#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/hosts/names/{name}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.host.HostUpdateRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.host.HostDetail&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Updates the enabled status of the requested host, or a 404 if not found. This endpoint is used to enable or disable scheduling nodes.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The name field corresponds to the host name, as described [[Getting_Started#Setting_Host_Names|here]]. Explicit host names should be set if you intend to rely known host names in this endpoint.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;enabled&amp;quot;: false&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| enabled || Y || Should this host should be enabled?&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as GET.&lt;br /&gt;
&lt;br /&gt;
= Event Hooks Endpoints =&lt;br /&gt;
&#039;&#039;As of Obsidian 5.4.0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==GET event hooks==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/event_hooks&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.host.EventHookListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Returns all event hooks across the cluster.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;hostEventHooks&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;host&amp;quot;: &amp;quot;prod-obsidian-RG1&amp;quot;,&lt;br /&gt;
        &amp;quot;eventHooks&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
                &amp;quot;eventHook&amp;quot;: &amp;quot;com.carfey.ops.event.dispatch.SlackEventHook&amp;quot;,&lt;br /&gt;
                &amp;quot;name&amp;quot;: &amp;quot;SlackEventHook&amp;quot;,&lt;br /&gt;
                &amp;quot;status&amp;quot;: &amp;quot;INACTIVE&amp;quot;,&lt;br /&gt;
                &amp;quot;registrationTime&amp;quot;: &amp;quot;2024-06-01T15:52:07-0400&amp;quot;,&lt;br /&gt;
                &amp;quot;heartbeat&amp;quot;: &amp;quot;2024-06-01T16:52:09-0400&amp;quot;,&lt;br /&gt;
                &amp;quot;markedInactiveAt&amp;quot;: &amp;quot;2024-06-01T16:52:09-0400&amp;quot; --if applicable&lt;br /&gt;
            }&lt;br /&gt;
        ]&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;host&amp;quot;: &amp;quot;prod-obsidian-HL1&amp;quot;,&lt;br /&gt;
        &amp;quot;eventHooks&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
                &amp;quot;eventHook&amp;quot;: &amp;quot;com.carfey.ops.event.dispatch.SlackEventHook&amp;quot;,&lt;br /&gt;
                &amp;quot;name&amp;quot;: &amp;quot;SlackEventHook&amp;quot;,&lt;br /&gt;
                &amp;quot;status&amp;quot;: &amp;quot;ACTIVE&amp;quot;,&lt;br /&gt;
                &amp;quot;registrationTime&amp;quot;: &amp;quot;2024-06-01T18:08:11-0400&amp;quot;,&lt;br /&gt;
                &amp;quot;heartbeat&amp;quot;: &amp;quot;2024-06-01T19:01:37-0400&amp;quot;&lt;br /&gt;
            }&lt;br /&gt;
        ]&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GET event hooks on host==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/hosts/{id}/event_hooks&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Alternate (by host name): &#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/hosts/names/{name}/event_hooks&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.host.EventHookListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Returns all event hooks for the given host.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;hostEventHooks&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;host&amp;quot;: &amp;quot;prod-obsidian-RG1&amp;quot;,&lt;br /&gt;
        &amp;quot;eventHooks&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
                &amp;quot;eventHook&amp;quot;: &amp;quot;com.carfey.ops.event.dispatch.SlackEventHook&amp;quot;,&lt;br /&gt;
                &amp;quot;name&amp;quot;: &amp;quot;SlackEventHook&amp;quot;,&lt;br /&gt;
                &amp;quot;status&amp;quot;: &amp;quot;INACTIVE&amp;quot;,&lt;br /&gt;
                &amp;quot;registrationTime&amp;quot;: &amp;quot;2024-06-01T15:52:07-0400&amp;quot;,&lt;br /&gt;
                &amp;quot;heartbeat&amp;quot;: &amp;quot;2024-06-01T16:52:09-0400&amp;quot;,&lt;br /&gt;
                &amp;quot;markedInactiveAt&amp;quot;: &amp;quot;2024-06-01T16:52:09-0400&amp;quot; --if applicable&lt;br /&gt;
            }&lt;br /&gt;
        ]&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==POST event hook pause or resume ==&lt;br /&gt;
&#039;&#039;As of Obsidian 5.5.0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;POST http(s)://localhost/rest/event_hooks&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Alternate (by host id): POST http(s)://localhost/rest/hosts/{id}/event_hooks&lt;br /&gt;
&lt;br /&gt;
Alternate (by host name): POST http(s)://localhost/rest/hosts/names/{name}/event_hooks &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.host.UpdateEventHookRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.host.EventHookListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Requests a specific event hook on a specific host to either Pause or Resume&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;hostName&amp;quot;: &amp;quot;obsidian-prod-1&amp;quot;,//only used when using non host-specific event_hooks endpoint&lt;br /&gt;
  &amp;quot;eventHook&amp;quot;: &amp;quot;com.carfey.ops.event.dispatch.SlackEventHook&amp;quot;,&lt;br /&gt;
  &amp;quot;eventHookName&amp;quot;: &amp;quot;SlackEventHook&amp;quot; &lt;br /&gt;
  &amp;quot;eventHookAction&amp;quot;: &amp;quot;PAUSE&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;hostEventHooks&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;host&amp;quot;: &amp;quot;obsidian-prod-1&amp;quot;,&lt;br /&gt;
        &amp;quot;eventHooks&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
                &amp;quot;eventHook&amp;quot;: &amp;quot;com.carfey.ops.event.dispatch.SlackEventHook&amp;quot;,&lt;br /&gt;
                &amp;quot;name&amp;quot;: &amp;quot;SlackEventHook&amp;quot;,&lt;br /&gt;
                &amp;quot;status&amp;quot;: &amp;quot;ACTIVE&amp;quot;,&lt;br /&gt;
                &amp;quot;registrationTime&amp;quot;: &amp;quot;2024-06-01T15:52:07-0400&amp;quot;,&lt;br /&gt;
                &amp;quot;heartbeat&amp;quot;: &amp;quot;2024-06-01T16:52:09-0400&amp;quot;,&lt;br /&gt;
                &amp;quot;requestedStatus&amp;quot; : &amp;quot;PAUSE&amp;quot;,&lt;br /&gt;
                &amp;quot;requestedBy&amp;quot; : &amp;quot;carey-ops&amp;quot;&lt;br /&gt;
            }&lt;br /&gt;
        ]&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Custom Calendar Endpoints =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The following endpoints allow for managing of [[Job_Features#Custom_Calendars|Custom Calendars]].&lt;br /&gt;
&lt;br /&gt;
==GET a list of custom calendars==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/calendars&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.calendar.CustomCalendarListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns a list of custom calendars.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;customCalendars&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;revision&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;dates&amp;quot;: [&amp;quot;2013-01-01&amp;quot;, &amp;quot;2013-02-18&amp;quot;, &amp;quot;2013-04-01&amp;quot;, &amp;quot;2013-05-20&amp;quot;],&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Corporate Holidays 2013&amp;quot;,&lt;br /&gt;
      &amp;quot;customCalendarId&amp;quot;: 1&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
      &amp;quot;revision&amp;quot;: 0,&lt;br /&gt;
      &amp;quot;dates&amp;quot;: [&amp;quot;2013-01-01&amp;quot;, &amp;quot;2013-02-18&amp;quot;, &amp;quot;2013-04-01&amp;quot;, &amp;quot;2013-05-20&amp;quot;],&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Corporate Holidays 2014&amp;quot;,&lt;br /&gt;
      &amp;quot;customCalendarId&amp;quot;: 2&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==GET details on an existing custom calendar==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/calendars/{id}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.calendar.CustomCalendar&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns the requested custom calendar, or a 404 if not found..&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;revision&amp;quot;: 0,&lt;br /&gt;
  &amp;quot;dates&amp;quot;: [&amp;quot;2013-01-01&amp;quot;, &amp;quot;2013-02-18&amp;quot;, &amp;quot;2013-04-01&amp;quot;, &amp;quot;2013-05-20&amp;quot;],&lt;br /&gt;
  &amp;quot;name&amp;quot;: &amp;quot;Corporate Holidays 2013&amp;quot;,&lt;br /&gt;
  &amp;quot;customCalendarId&amp;quot;: 1&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==POST a new custom calendar ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;POST http(s)://localhost/rest/calendars/&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.calendar.CustomCalendarUpdateRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.calendar.CustomCalendar&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Updates the name and dates of the requested calendar, or a 404 if not found.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;name&amp;quot;: &amp;quot;Cal1&amp;quot;,&lt;br /&gt;
   &amp;quot;dates&amp;quot;: [&amp;quot;2016-10-14&amp;quot; ,&amp;quot;2018-06-14&amp;quot;],&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| name|| Y || Calendar name&lt;br /&gt;
|- &lt;br /&gt;
| dates|| Y || Dates to exclude&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as GET.&lt;br /&gt;
&lt;br /&gt;
==PUT updates to an existing custom calendar==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;PUT http(s)://localhost/rest/calendars/{id}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.calendar.CustomCalendarUpdateRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.calendar.CustomCalendar&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Updates the name and dates of the requested calendar, or a 404 if not found.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Cal2&amp;quot;,&lt;br /&gt;
    &amp;quot;dates&amp;quot;: [&amp;quot;2018-06-13&amp;quot;,&amp;quot;2016-10-13&amp;quot;]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| name|| Y || Calendar name&lt;br /&gt;
|- &lt;br /&gt;
| dates|| Y || Dates to exclude&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as POST.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==DELETE an existing custom calendar==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;DELETE http(s)://localhost/rest/calendars/{id}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.calendar.CustomCalendar&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Deletes a custom calendar. Returns a 404 if not found.&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as GET, and return the final state of the calendar before the delete.&lt;br /&gt;
&lt;br /&gt;
= Subscriber Endpoints =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==GET a list of subscribers==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/subscribers&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.notification.SubscriberListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns a list of configured notification subscribers.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{  &lt;br /&gt;
   &amp;quot;subscribers&amp;quot;: [  &lt;br /&gt;
      {  &lt;br /&gt;
         &amp;quot;id&amp;quot;: 1,&lt;br /&gt;
         &amp;quot;emailAddress&amp;quot;: &amp;quot;test@example.com&amp;quot;,&lt;br /&gt;
         &amp;quot;active&amp;quot;: true,&lt;br /&gt;
         &amp;quot;generalSubscriptions&amp;quot;: [  &lt;br /&gt;
            {  &lt;br /&gt;
               &amp;quot;category&amp;quot;: &amp;quot;QUEUE&amp;quot;,&lt;br /&gt;
               &amp;quot;level&amp;quot;: &amp;quot;ERROR&amp;quot;,&lt;br /&gt;
               &amp;quot;active&amp;quot;: true,&lt;br /&gt;
            },&lt;br /&gt;
            {  &lt;br /&gt;
               &amp;quot;category&amp;quot;: &amp;quot;JOB&amp;quot;,&lt;br /&gt;
               &amp;quot;level&amp;quot;: &amp;quot;ERROR&amp;quot;,&lt;br /&gt;
               &amp;quot;active&amp;quot;: true,&lt;br /&gt;
	       &amp;quot;job&amp;quot; : { // only exists when the subscription applies to a specific job&lt;br /&gt;
                  &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
                  &amp;quot;jobId&amp;quot;: 1,&lt;br /&gt;
                  &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
                  &amp;quot;nickname&amp;quot;: &amp;quot;Cleanup Job&amp;quot;,&lt;br /&gt;
                  &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
                  &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.JobHistoryCleanupJob&amp;quot;,&lt;br /&gt;
                  &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
                  &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
                  &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
                  &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
                  &amp;quot;hostPreference&amp;quot;: true,&lt;br /&gt;
                  &amp;quot;revision&amp;quot;: 52&lt;br /&gt;
               }&lt;br /&gt;
            }&lt;br /&gt;
         ],&lt;br /&gt;
         &amp;quot;jobExecutionSubscriptions&amp;quot;: [  &lt;br /&gt;
            {  &lt;br /&gt;
               &amp;quot;allJobs&amp;quot;: true,&lt;br /&gt;
               &amp;quot;jobs&amp;quot;: [], // always empty when allJobs is true&lt;br /&gt;
               &amp;quot;triggerStates&amp;quot;: [ &amp;quot;DIED&amp;quot;, &amp;quot;FAILED&amp;quot;, &amp;quot;RECOVERY&amp;quot; ],&lt;br /&gt;
               &amp;quot;resultConditions&amp;quot;: [], // present when CONDITIONAL triggerState is used&lt;br /&gt;
               &amp;quot;active&amp;quot;: false&lt;br /&gt;
            },&lt;br /&gt;
            {  &lt;br /&gt;
               &amp;quot;allJobs&amp;quot;: false,&lt;br /&gt;
               &amp;quot;jobs&amp;quot;: [  &lt;br /&gt;
                  {  &lt;br /&gt;
                     &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
                     &amp;quot;jobId&amp;quot;: 1,&lt;br /&gt;
                     &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
                     &amp;quot;nickname&amp;quot;: &amp;quot;Cleanup Job&amp;quot;,&lt;br /&gt;
                     &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
                     &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.JobHistoryCleanupJob&amp;quot;,&lt;br /&gt;
                     &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
                     &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
                     &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
                     &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
                     &amp;quot;hostPreference&amp;quot;: true,&lt;br /&gt;
                     &amp;quot;revision&amp;quot;: 52&lt;br /&gt;
                  }&lt;br /&gt;
               ],&lt;br /&gt;
               &amp;quot;triggerStates&amp;quot;: [ &amp;quot;CONDITIONAL&amp;quot; ],&lt;br /&gt;
               &amp;quot;resultConditions&amp;quot;: [  &lt;br /&gt;
                  {  &lt;br /&gt;
                     &amp;quot;variableName&amp;quot;: &amp;quot;cleanupState&amp;quot;,&lt;br /&gt;
                     &amp;quot;operator&amp;quot;: &amp;quot;EQUALS&amp;quot;,&lt;br /&gt;
                     &amp;quot;values&amp;quot;: [ &amp;quot;incomplete&amp;quot; ] // depending on the operator, values may be empty, have 1 value, or multiple.&lt;br /&gt;
                  },&lt;br /&gt;
                  {  &lt;br /&gt;
                     &amp;quot;variableName&amp;quot;: &amp;quot;warnings&amp;quot;,&lt;br /&gt;
                     &amp;quot;operator&amp;quot;: &amp;quot;EXISTS&amp;quot;,&lt;br /&gt;
                     &amp;quot;values&amp;quot;: []&lt;br /&gt;
                  }&lt;br /&gt;
               ],&lt;br /&gt;
               &amp;quot;active&amp;quot;: true&lt;br /&gt;
            }&lt;br /&gt;
         ]&lt;br /&gt;
      },&lt;br /&gt;
      {  &lt;br /&gt;
         &amp;quot;id&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;emailAddress&amp;quot;: &amp;quot;inactive@example.com&amp;quot;,&lt;br /&gt;
         &amp;quot;active&amp;quot;: false,&lt;br /&gt;
         &amp;quot;generalSubscriptions&amp;quot;: [],&lt;br /&gt;
         &amp;quot;jobExecutionSubscriptions&amp;quot;: []&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GET details of an existing subscriber==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/subscribers/{subscriberId}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.notification.Subscriber&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns details of an existing subscriber, or a 404 if not found. Contains the same set of fields as the listing endpoint. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{  &lt;br /&gt;
   &amp;quot;id&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;emailAddress&amp;quot;: &amp;quot;test@example.com&amp;quot;,&lt;br /&gt;
   &amp;quot;active&amp;quot;: true,&lt;br /&gt;
   &amp;quot;generalSubscriptions&amp;quot;: [  &lt;br /&gt;
      {  &lt;br /&gt;
         &amp;quot;active&amp;quot;: true,&lt;br /&gt;
         &amp;quot;category&amp;quot;: &amp;quot;QUEUE&amp;quot;,&lt;br /&gt;
         &amp;quot;level&amp;quot;: &amp;quot;ERROR&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
   ],&lt;br /&gt;
   &amp;quot;jobExecutionSubscriptions&amp;quot;: [  &lt;br /&gt;
      {  &lt;br /&gt;
         &amp;quot;active&amp;quot;: false,&lt;br /&gt;
         &amp;quot;allJobs&amp;quot;: true,&lt;br /&gt;
         &amp;quot;jobs&amp;quot;: [],&lt;br /&gt;
         &amp;quot;triggerStates&amp;quot;: [ &amp;quot;DIED&amp;quot;, &amp;quot;FAILED&amp;quot;, &amp;quot;RECOVERY&amp;quot; ],&lt;br /&gt;
         &amp;quot;resultConditions&amp;quot;: []&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==POST a new subscriber==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;POST http(s)://localhost/rest/subscribers&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.notification.SubscriberUpdateRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.notification.Subscriber&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Creates a new notification subscriber.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{  &lt;br /&gt;
   &amp;quot;emailAddress&amp;quot;: &amp;quot;test@example.com&amp;quot;,&lt;br /&gt;
   &amp;quot;active&amp;quot;: true,&lt;br /&gt;
   &amp;quot;generalSubscriptions&amp;quot;: [  &lt;br /&gt;
      {  &lt;br /&gt;
         &amp;quot;category&amp;quot;: &amp;quot;JOB&amp;quot;,&lt;br /&gt;
         &amp;quot;level&amp;quot;: &amp;quot;ERROR&amp;quot;,&lt;br /&gt;
         &amp;quot;active&amp;quot;: true,&lt;br /&gt;
         &amp;quot;jobId&amp;quot;: 123 // optional, and only valid when a job-related category is selected&lt;br /&gt;
      }&lt;br /&gt;
   ],&lt;br /&gt;
   &amp;quot;jobExecutionSubscriptions&amp;quot;:[  &lt;br /&gt;
      {  &lt;br /&gt;
         &amp;quot;active&amp;quot;: false,&lt;br /&gt;
         &amp;quot;allJobs&amp;quot;: true,&lt;br /&gt;
         &amp;quot;triggerStates&amp;quot;: [ &amp;quot;DIED&amp;quot;, &amp;quot;FAILED&amp;quot;, &amp;quot;RECOVERY&amp;quot; ]&lt;br /&gt;
      },&lt;br /&gt;
      {  &lt;br /&gt;
         &amp;quot;active&amp;quot;: true,&lt;br /&gt;
         &amp;quot;jobIds&amp;quot;: [ 123 ], // should not be supplied with the allJobs flag which takes precedence&lt;br /&gt;
         &amp;quot;triggerStates&amp;quot;: [ &amp;quot;CONDITIONAL&amp;quot; ],&lt;br /&gt;
         &amp;quot;resultConditions&amp;quot;: [  &lt;br /&gt;
            {  &lt;br /&gt;
               &amp;quot;variableName&amp;quot;: &amp;quot;cleanupState&amp;quot;,&lt;br /&gt;
               &amp;quot;operator&amp;quot;: &amp;quot;EQUALS&amp;quot;,&lt;br /&gt;
               &amp;quot;values&amp;quot;: [ &amp;quot;incomplete&amp;quot; ] // depending on the operator, values may be empty, have 1 value, or multiple.&lt;br /&gt;
            }&lt;br /&gt;
         ]&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| emailAddress|| Y || Valid email address of the subscriber. Must be unique.&lt;br /&gt;
|- &lt;br /&gt;
| active || N || Whether the subscription is active. Defaults to false.&lt;br /&gt;
|- &lt;br /&gt;
| generalSubscriptions|| N || Zero or more general subscriptions. Each item contains a required subscription &amp;lt;code&amp;gt;category&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;level&amp;lt;/code&amp;gt; as defined in [[Unified_API#Enumerations|Enumerations]], an optional &amp;lt;code&amp;gt;jobId&amp;lt;/code&amp;gt; and an optional &amp;lt;code&amp;gt;active&amp;lt;/code&amp;gt; flag which defaults to false.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Only &amp;lt;code&amp;gt;ERROR&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;WARNING&amp;lt;/code&amp;gt; AND &amp;lt;code&amp;gt;INFO&amp;lt;/code&amp;gt; are valid values for &amp;lt;code&amp;gt;level&amp;lt;/code&amp;gt;. Only &amp;lt;code&amp;gt;JOB&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;JOB_CHAIN&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;JOB_CONFIG&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;JOB_RECOVERY&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LICENCE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;QUEUE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SYSTEM_PARAMETER&amp;lt;/code&amp;gt; and null are valid values for &amp;lt;code&amp;gt;category&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| jobExecutionSubscriptions || N || Zero or more job execution subscriptions. Each item contains a required &amp;lt;code&amp;gt;triggerStates&amp;lt;/code&amp;gt; field containing at least one subscription job status as defined in [[Unified_API#Enumerations|Enumerations]]. Note that &amp;lt;code&amp;gt;CONDITIONAL&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;COMPLETED&amp;lt;/code&amp;gt; types cannot be used on the same chain.&lt;br /&gt;
&lt;br /&gt;
If the &amp;lt;code&amp;gt;CONDITIONAL&amp;lt;/code&amp;gt; state is selected, at least one condition must be supplied in the &amp;lt;code&amp;gt;resultConditions&amp;lt;/code&amp;gt; field. Result conditions consist of a &amp;lt;code&amp;gt;variableName&amp;lt;/code&amp;gt;, an &amp;lt;code&amp;gt;operator&amp;lt;/code&amp;gt; as defined in [[Unified_API#Enumerations|Enumerations]], and for most operators, a list of &amp;lt;code&amp;gt;values&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;EXISTS&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;NOT EXISTS&amp;lt;/code&amp;gt; operators do not use values, so they must not be supplied. Operators &amp;lt;code&amp;gt;IN&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;NOT IN&amp;lt;/code&amp;gt; support one or more values, and all other operators accept a single value in the &amp;lt;code&amp;gt;values&amp;lt;/code&amp;gt; list. Values for this field map to bean class &amp;lt;code&amp;gt;com.carfey.ops.api.enums.JobSubscriptionConditionOperator&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In addition, two additional optional fields control which jobs the subscription applies to. &amp;lt;code&amp;gt;jobIds&amp;lt;/code&amp;gt; is used to specify specific jobs, while &amp;lt;code&amp;gt;allJobs&amp;lt;/code&amp;gt; may be set to true to make it apply to all jobs. If &amp;lt;code&amp;gt;jobIds&amp;lt;/code&amp;gt; is supplied and &amp;lt;code&amp;gt;allJobs&amp;lt;/code&amp;gt; is set to true, &amp;lt;code&amp;gt;jobIds&amp;lt;/code&amp;gt; will be ignored.&lt;br /&gt;
&lt;br /&gt;
Finally, the &amp;lt;code&amp;gt;active&amp;lt;/code&amp;gt; flag may be supplied which defaults to true.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as GET.&lt;br /&gt;
&lt;br /&gt;
==PUT updates to an existing subscriber ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;PUT http(s)://localhost/rest/subscribers/{subscriberId}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.notification.SubscriberUpdateRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.notification.Subscriber&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Updates an existing subscriber. Returns a 404 if not found.&lt;br /&gt;
&lt;br /&gt;
Requests and responses have the same format as POST.&lt;br /&gt;
&lt;br /&gt;
==DELETE an existing subscriber==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;DELETE http(s)://localhost/rest/subscribers/{subscriberId}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.notification.Subscriber&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Deletes a subscriber. Returns a 404 if not found.&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as GET, and return the final state of the subscriber before the delete.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Templates Endpoints =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
These endpoints allow for managing notification templates.&lt;br /&gt;
&lt;br /&gt;
==GET a list of templates==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/templates&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.notification.TemplateListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns a list of configured templates.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{  &lt;br /&gt;
   &amp;quot;templates&amp;quot;:[  &lt;br /&gt;
      {  &lt;br /&gt;
         &amp;quot;id&amp;quot;: 1,&lt;br /&gt;
         &amp;quot;name&amp;quot;: &amp;quot;Obsidian Default Job Template&amp;quot;,&lt;br /&gt;
         &amp;quot;active&amp;quot;: true,&lt;br /&gt;
         &amp;quot;category&amp;quot;: &amp;quot;JOB&amp;quot;,&lt;br /&gt;
         &amp;quot;defaultForJobs&amp;quot;: true, // indicates if it is the default job template for the category&lt;br /&gt;
         &amp;quot;jobs&amp;quot;: [ // only present for job categories when defaultForJobs is false&lt;br /&gt;
            {  &lt;br /&gt;
               &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
               &amp;quot;jobId&amp;quot;: 1,&lt;br /&gt;
               &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
               &amp;quot;nickname&amp;quot;: &amp;quot;Cleanup Job&amp;quot;,&lt;br /&gt;
               &amp;quot;folder&amp;quot;: &amp;quot;Production/Test&amp;quot;, // as of 4.1.0&lt;br /&gt;
               &amp;quot;interruptable&amp;quot;: true,&lt;br /&gt;
               &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.JobHistoryCleanupJob&amp;quot;,&lt;br /&gt;
               &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
               &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
               &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
               &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
               &amp;quot;hostPreference&amp;quot;: true,&lt;br /&gt;
               &amp;quot;revision&amp;quot;: 52&lt;br /&gt;
               }&lt;br /&gt;
          ],&lt;br /&gt;
         &amp;quot;subjectTemplate&amp;quot;: &amp;quot;Obsidian [{{hostName}}] {{subject}}&amp;quot;,&lt;br /&gt;
         &amp;quot;bodyTemplate&amp;quot;: &amp;quot;Obsidian {{hostName}} Body content...&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      {  &lt;br /&gt;
         &amp;quot;id&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;name&amp;quot;: &amp;quot;Obsidian Default Other Template&amp;quot;,&lt;br /&gt;
         &amp;quot;active&amp;quot;: true,&lt;br /&gt;
         &amp;quot;defaultForJobs&amp;quot;: false,&lt;br /&gt;
         &amp;quot;subjectTemplate&amp;quot;: &amp;quot;Obsidian [{{hostName}}] {{subject}}&amp;quot;,&lt;br /&gt;
         &amp;quot;bodyTemplate&amp;quot;: &amp;quot;Obsidian {{hostName}} Body content...&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GET details of an existing template==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/templates/{templateId}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.notification.Template&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns details of a configured template, or a 404 if not found. Contains the same set of fields as the template listing endpoint. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{  &lt;br /&gt;
   &amp;quot;id&amp;quot;: 2,&lt;br /&gt;
   &amp;quot;name&amp;quot;: &amp;quot;Obsidian Default Other Template&amp;quot;,&lt;br /&gt;
   &amp;quot;active&amp;quot;: true,&lt;br /&gt;
   &amp;quot;category&amp;quot;: &amp;quot;LICENCE&amp;quot;, // if missing, it is the default generic template&lt;br /&gt;
   &amp;quot;defaultForJobs&amp;quot;: false,&lt;br /&gt;
   &amp;quot;subjectTemplate&amp;quot;: &amp;quot;Obsidian [{{hostName}}] {{subject}}&amp;quot;,&lt;br /&gt;
   &amp;quot;bodyTemplate&amp;quot;: &amp;quot;Obsidian {{hostName}} Body content...&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==POST a new template==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;POST http(s)://localhost/rest/templates&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.notification.TemplateUpdateRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.notification.Template&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Creates a new template.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;name&amp;quot;: &amp;quot;Cleanup Job Template&amp;quot;,&lt;br /&gt;
   &amp;quot;active&amp;quot;: true,&lt;br /&gt;
   &amp;quot;category&amp;quot;: &amp;quot;JOB&amp;quot;, // category not required when it is the default template&lt;br /&gt;
   &amp;quot;defaultForJobs&amp;quot;: false, // when true, jobIds are ignored&lt;br /&gt;
   &amp;quot;jobIds&amp;quot;: [ 123, 456 ],&lt;br /&gt;
   &amp;quot;subjectTemplate&amp;quot;: &amp;quot;Obsidian [{{hostName}}] {{subject}}&amp;quot;,&lt;br /&gt;
   &amp;quot;bodyTemplate&amp;quot;: &amp;quot;Obsidian {{hostName}} Body content...&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| name || Y || Unique name of the template.&lt;br /&gt;
|- &lt;br /&gt;
| active || N || Whether the template is active. Defaults to false.&lt;br /&gt;
|-&lt;br /&gt;
| category || N || The category for the template as defined in [[Unified_API#Enumerations|Enumerations]], or null if it is the default generic template. Supports multiple values. Not all categories are supported. Only &amp;lt;code&amp;gt;JOB&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;JOB_CHAIN&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;JOB_CONFIG&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;JOB_RECOVERY&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LICENCE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;QUEUE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SYSTEM_PARAMETER&amp;lt;/code&amp;gt; and null are valid values. &lt;br /&gt;
|- &lt;br /&gt;
| defaultForJobs || N || For job-related categories, setting this to true allows for a template to be used as the default template when one isn&#039;t assigned to a particular job. Defaults to false.&lt;br /&gt;
|- &lt;br /&gt;
| jobIds || N || For job-related categories, specific jobs may be assigned to use this template.&lt;br /&gt;
|- &lt;br /&gt;
| subjectTemplate || Y || A valid [[Email_Templates|Mustache template]] for the subject.&lt;br /&gt;
|- &lt;br /&gt;
| bodyTemplate || Y || A valid [[Email_Templates|Mustache template]] for the body.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as GET.&lt;br /&gt;
&lt;br /&gt;
==PUT updates to an existing template==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;PUT http(s)://localhost/rest/templates/{templateId}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.notification.TemplateUpdateRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.notification.Template&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Requests and responses have the same format as POST.&lt;br /&gt;
&lt;br /&gt;
==DELETE an existing template ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;DELETE http(s)://localhost/rest/templates/{templateId}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.notification.Template&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Deletes a template. Returns a 404 if not found.&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as GET, and return the final state of the template before the delete.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Notification Endpoints =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
These endpoints allow you to query what notifications were triggered in Obsidian. Note that existence of records does not necessarily indicate the notification was successfully sent or received.&lt;br /&gt;
&lt;br /&gt;
==GET a list of notifications==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/notifications[?category=QUEUE&amp;amp;level=ERROR&amp;amp;start=1356987599000&amp;amp;end=1357510546000&amp;amp;startKey=123]&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.notification.NotificationListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns a list of notifications, optionally filtered by query string parameters. Results are ordered roughly according to when they were created, but ordering is not guaranteed to be in order of created time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt; field in the response indicates that there were too many results to return (i.e. exceeded &amp;lt;code&amp;gt;maxRecords&amp;lt;/code&amp;gt; as configured in the [[Admin Scheduler Settings|scheduler settings]] screen). To fetch the next page of results, invoke the same endpoint with the &amp;lt;code&amp;gt;startKey&amp;lt;/code&amp;gt; query string parameter set to the returned &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Query String Parameters&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|-&lt;br /&gt;
| category || N || The category of the notifications as defined in [[Unified_API#Enumerations|Enumerations]]. Supports multiple values.&lt;br /&gt;
|-&lt;br /&gt;
| level || N || The logging level of the notifications defined in [[Unified_API#Enumerations|Enumerations]]. Supports multiple values.&lt;br /&gt;
|- &lt;br /&gt;
| start || N || Start date for the notifications to return (inclusive).  Defaults to the current minute.&lt;br /&gt;
|- &lt;br /&gt;
| end || N || Start date for the notifications to return (inclusive). Defaults to a day after the start time. Must be after the start time.&lt;br /&gt;
|- &lt;br /&gt;
| startKey || N || If requesting the next page of results from a previous call, set it to the returned &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;nextPageStartKey&amp;quot;: &amp;quot;123&amp;quot;,&lt;br /&gt;
  &amp;quot;notifications&amp;quot;: [  &lt;br /&gt;
      {  &lt;br /&gt;
         &amp;quot;id&amp;quot;: 51,&lt;br /&gt;
         &amp;quot;log&amp;quot;: {  &lt;br /&gt;
            &amp;quot;id&amp;quot;: 292,&lt;br /&gt;
            &amp;quot;eventTime&amp;quot;: &amp;quot;2015-01-22T16:44:00-0800&amp;quot;,&lt;br /&gt;
            &amp;quot;host&amp;quot;: &amp;quot;Demo&amp;quot;,&lt;br /&gt;
            &amp;quot;category&amp;quot;: &amp;quot;JOB_RUN&amp;quot;,&lt;br /&gt;
            &amp;quot;level&amp;quot;: &amp;quot;INFO&amp;quot;,&lt;br /&gt;
            &amp;quot;summary&amp;quot;: &amp;quot;Completed job [Job History Cleanup 033] scheduled for [2015-01-22 16:44:00].&amp;quot;&lt;br /&gt;
         },&lt;br /&gt;
         &amp;quot;subscriber&amp;quot;:{  &lt;br /&gt;
            &amp;quot;id&amp;quot;: 51,&lt;br /&gt;
            &amp;quot;emailAddress&amp;quot;: &amp;quot;test@test.com&amp;quot;,&lt;br /&gt;
            &amp;quot;active&amp;quot;: true // current active state (not when triggered)&lt;br /&gt;
         }&lt;br /&gt;
      },&lt;br /&gt;
      {  &lt;br /&gt;
         &amp;quot;id&amp;quot;: 52,&lt;br /&gt;
         &amp;quot;log&amp;quot;: {  &lt;br /&gt;
            &amp;quot;id&amp;quot;: 295,&lt;br /&gt;
            &amp;quot;eventTime&amp;quot;: &amp;quot;2015-01-22T16:44:00-0800&amp;quot;,&lt;br /&gt;
            &amp;quot;host&amp;quot;: &amp;quot;Demo&amp;quot;,&lt;br /&gt;
            &amp;quot;category&amp;quot;: &amp;quot;JOB_RUN&amp;quot;,&lt;br /&gt;
            &amp;quot;level&amp;quot;: &amp;quot;INFO&amp;quot;,&lt;br /&gt;
            &amp;quot;summary&amp;quot;: &amp;quot;Completed job [Job History Cleanup 145] scheduled for [2015-01-22 16:44:00].&amp;quot;&lt;br /&gt;
         },&lt;br /&gt;
         &amp;quot;subscriber&amp;quot;: {  &lt;br /&gt;
            &amp;quot;id&amp;quot;: 51,&lt;br /&gt;
            &amp;quot;emailAddress&amp;quot;: &amp;quot;test@test.com&amp;quot;,&lt;br /&gt;
            &amp;quot;active&amp;quot;: true&lt;br /&gt;
         }&lt;br /&gt;
      },&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GET details of an existing notification==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/notifications/{id}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.notification.Notification&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns detail of an existing notification, which contains the same fields as the listing endpoint. Returns a 404 if not found.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{  &lt;br /&gt;
   &amp;quot;id&amp;quot;: 51,&lt;br /&gt;
   &amp;quot;log&amp;quot;: {  &lt;br /&gt;
      &amp;quot;id&amp;quot;: 292,&lt;br /&gt;
      &amp;quot;eventTime&amp;quot;: &amp;quot;2015-01-22T16:44:00-0800&amp;quot;,&lt;br /&gt;
      &amp;quot;host&amp;quot;: &amp;quot;Demo&amp;quot;,&lt;br /&gt;
      &amp;quot;category&amp;quot;: &amp;quot;JOB_RUN&amp;quot;,&lt;br /&gt;
      &amp;quot;level&amp;quot;: &amp;quot;INFO&amp;quot;,&lt;br /&gt;
      &amp;quot;summary&amp;quot;: &amp;quot;Completed job [Job History Cleanup 033] scheduled for [2015-01-22 16:44:00].&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;subscriber&amp;quot;:{  &lt;br /&gt;
      &amp;quot;id&amp;quot;: 51,&lt;br /&gt;
      &amp;quot;emailAddress&amp;quot;: &amp;quot;test@test.com&amp;quot;,&lt;br /&gt;
      &amp;quot;active&amp;quot;: true // current active state (not when triggered)&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Log Endpoints =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==GET a list of logs==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/logs[?host=host1&amp;amp;filterText=error&amp;amp;category=QUEUE&amp;amp;level=ERROR&amp;amp;start=1356987599000&amp;amp;end=1357510546000&amp;amp;startKey=123]&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.log.LogListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns a list of event logs, optionally filtered by query string parameters. Results are ordered roughly according to when they were created, but ordering is not guaranteed to be in order of created time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt; field in the response indicates that there were too many results to return (i.e. exceeded &amp;lt;code&amp;gt;maxRecords&amp;lt;/code&amp;gt; as configured in the [[Admin Scheduler Settings|scheduler settings]] screen). To fetch the next page of results, invoke the same endpoint with the &amp;lt;code&amp;gt;startKey&amp;lt;/code&amp;gt; query string parameter set to the returned &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Query String Parameters&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|-&lt;br /&gt;
| host || N || If specified, only logs from the specified host name(s) are included. Supports multiple values.&lt;br /&gt;
|-&lt;br /&gt;
| filterText || N || If specified, only messages containing the supplied filter text are returned. &#039;%&#039; can be used as a wildcard.&lt;br /&gt;
|-&lt;br /&gt;
| category || N || The category of the log as defined in [[Unified_API#Enumerations|Enumerations]]. Supports multiple values.&lt;br /&gt;
|-&lt;br /&gt;
| level || N || The logging level of the log as defined in [[Unified_API#Enumerations|Enumerations]]. Supports multiple values.&lt;br /&gt;
|- &lt;br /&gt;
| start || N || Start date for the logs to return (inclusive).  Defaults to the current minute.&lt;br /&gt;
|- &lt;br /&gt;
| end || N || Start date for the logs to return (inclusive). Defaults to a day after the start time. Must be after the start time.&lt;br /&gt;
|- &lt;br /&gt;
| startKey || N || If requesting the next page of results from a previous call, set it to the returned &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;nextPageStartKey&amp;quot; : &amp;quot;123&amp;quot;,&lt;br /&gt;
  &amp;quot;logs&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
	   &amp;quot;id&amp;quot;: 1619,&lt;br /&gt;
	   &amp;quot;eventTime&amp;quot;: &amp;quot;2015-01-21T13:59:43-0800&amp;quot;,&lt;br /&gt;
	   &amp;quot;host&amp;quot;: &amp;quot;Demo&amp;quot;,&lt;br /&gt;
	   &amp;quot;category&amp;quot;: &amp;quot;JOB_SPAWNER&amp;quot;,&lt;br /&gt;
	   &amp;quot;level&amp;quot;: &amp;quot;DEBUG&amp;quot;,&lt;br /&gt;
	   &amp;quot;summary&amp;quot;: &amp;quot;Spawning [Test Job] scheduled for 2015-01-21 13:59:00&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	{&lt;br /&gt;
	   &amp;quot;id&amp;quot;: 4,&lt;br /&gt;
	   &amp;quot;eventTime&amp;quot;: &amp;quot;2015-01-20T11:54:49-0800&amp;quot;,&lt;br /&gt;
	   &amp;quot;host&amp;quot;: &amp;quot;Demo&amp;quot;,&lt;br /&gt;
	   &amp;quot;category&amp;quot;: &amp;quot;LICENCE&amp;quot;,&lt;br /&gt;
	   &amp;quot;level&amp;quot;: &amp;quot;INFO&amp;quot;,&lt;br /&gt;
	   &amp;quot;summary&amp;quot;: &amp;quot;Successfully refreshed licence from server https://licence.carfey.com/licence for 120 minutes.&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GET details of an existing log==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/logs/{logId}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.log.Log&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns detail of an existing log entry. Returns a 404 if not found.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;id&amp;quot;: 1619,&lt;br /&gt;
   &amp;quot;eventTime&amp;quot;: &amp;quot;2015-01-21T13:59:43-0800&amp;quot;,&lt;br /&gt;
   &amp;quot;host&amp;quot;: &amp;quot;Demo&amp;quot;,&lt;br /&gt;
   &amp;quot;category&amp;quot;: &amp;quot;JOB_SPAWNER&amp;quot;,&lt;br /&gt;
   &amp;quot;level&amp;quot;: &amp;quot;DEBUG&amp;quot;,&lt;br /&gt;
   &amp;quot;summary&amp;quot;: &amp;quot;Spawning [Test Job] scheduled for 2015-01-21 13:59:00&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= System Parameter Endpoints =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==GET a list of system parameters==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/system_parameters&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.system.SystemParameterListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns a list of system parameters which can be edited.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;parameters&amp;quot;: [&lt;br /&gt;
     {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;clientKeyServerUrl&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot;: &amp;quot;Address of the primary key server. If running a proxy, this should be the address of the proxy server.&amp;quot;,&lt;br /&gt;
        &amp;quot;type&amp;quot;: &amp;quot;STRING&amp;quot;,&lt;br /&gt;
        &amp;quot;category&amp;quot;: &amp;quot;LICENCE&amp;quot;,&lt;br /&gt;
        &amp;quot;value&amp;quot;: &amp;quot;https://licence.carfey.com/licence&amp;quot;&lt;br /&gt;
     },&lt;br /&gt;
     {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;maxJobThreads&amp;quot;, &lt;br /&gt;
        &amp;quot;description&amp;quot;: &amp;quot;This value determines the maximum number of threads that will be spawned for running jobs.  Changes to this value require a server restart.&amp;quot;,&lt;br /&gt;
        &amp;quot;type&amp;quot;: &amp;quot;INTEGER&amp;quot;,&lt;br /&gt;
        &amp;quot;category&amp;quot;: &amp;quot;JOB_SPAWNER&amp;quot;,&lt;br /&gt;
        &amp;quot;value&amp;quot;: &amp;quot;1000&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GET details of a system parameter ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/system_parameters/{name}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.system.SystemParameter&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns details of a system parameter, or a 404 if not found. Contains the same set of fields as the listing endpoint. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;name&amp;quot;: &amp;quot;clientKeyServerUrl&amp;quot;,&lt;br /&gt;
   &amp;quot;description&amp;quot;: &amp;quot;Address of the primary key server. If running a proxy, this should be the address of the proxy server.&amp;quot;,&lt;br /&gt;
   &amp;quot;type&amp;quot;: &amp;quot;STRING&amp;quot;,&lt;br /&gt;
   &amp;quot;category&amp;quot;: &amp;quot;LICENCE&amp;quot;,&lt;br /&gt;
   &amp;quot;value&amp;quot;: &amp;quot;https://licence.carfey.com/licence&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==PUT updates to a system parameter ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;PUT http(s)://localhost/rest/system_parameters/{name}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.system.SystemParameterUpdateRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.system.SystemParameter&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Updates a system parameter&#039;s value. Values are accepted as strings, but must be valid for their target type (integer, boolean, etc.). Returns a 404 if not found.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;value&amp;quot;: &amp;quot;60&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| value || N || The new value for the system parameter, which can be converted into the appropriate target type. Some string values support an empty value.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as GET.&lt;br /&gt;
&lt;br /&gt;
= User Endpoints =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note that user endpoints are only available when using Obsidian&#039;s native authentication, and do not support LDAP or custom authentication.&lt;br /&gt;
&lt;br /&gt;
==GET a list of users==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/users&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.user.UserListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns a list of configured users.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;users&amp;quot;: [&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;id&amp;quot;: 1,&lt;br /&gt;
        &amp;quot;userName&amp;quot;: &amp;quot;admin&amp;quot;,&lt;br /&gt;
        &amp;quot;active&amp;quot;: true,&lt;br /&gt;
        &amp;quot;roles&amp;quot;: [&amp;quot;ADMIN&amp;quot;,&amp;quot;API&amp;quot;]&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
	&amp;quot;id&amp;quot;: 2&lt;br /&gt;
        &amp;quot;userName&amp;quot;: &amp;quot;reader&amp;quot;,&lt;br /&gt;
	&amp;quot;active&amp;quot;: false,&lt;br /&gt;
	&amp;quot;roles&amp;quot;: [] // no roles means read-only&lt;br /&gt;
    }&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GET details of an existing user==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/users/{userId}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.user.User&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns details of a configured user, or a 404 if not found. Contains the same set of fields as the user listing endpoint. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;id&amp;quot;: 1,&lt;br /&gt;
   &amp;quot;userName&amp;quot;: &amp;quot;admin&amp;quot;,&lt;br /&gt;
   &amp;quot;active&amp;quot;: true,&lt;br /&gt;
   &amp;quot;roles&amp;quot;: [&amp;quot;ADMIN&amp;quot;,&amp;quot;API&amp;quot;]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==POST a new user==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;POST http(s)://localhost/rest/users&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.user.UserCreationRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.user.User&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Creates a new user.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;userName&amp;quot;: &amp;quot;admin&amp;quot;,&lt;br /&gt;
   &amp;quot;active&amp;quot;: true,&lt;br /&gt;
   &amp;quot;roles&amp;quot;: [&amp;quot;ADMIN&amp;quot;,&amp;quot;API&amp;quot;],&lt;br /&gt;
   &amp;quot;password&amp;quot;: &amp;quot;changeme&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| userName || Y || Unique user name used to log in.&lt;br /&gt;
|- &lt;br /&gt;
| active || N || Whether the user is enabled. Defaults to false.&lt;br /&gt;
|- &lt;br /&gt;
| roles || N || Zero or more roles as defined in [[Unified_API#Enumerations|Enumerations]]. Supplying no roles indicates it is a normal read-only user.&lt;br /&gt;
|- &lt;br /&gt;
| password || Y || A password at least 6 characters long.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as GET.&lt;br /&gt;
&lt;br /&gt;
==PUT updates to an existing user==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;PUT http(s)://localhost/rest/users/{userId}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.user.UserUpdateRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.user.User&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Updates an existing user. Returns a 404 if not found. Semantics similar to a PATCH request are used, so that only supplied fields are updated. User names cannot be updated. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;active&amp;quot;: true,&lt;br /&gt;
   &amp;quot;roles&amp;quot;: [&amp;quot;ADMIN&amp;quot;,&amp;quot;API&amp;quot;],&lt;br /&gt;
   &amp;quot;password&amp;quot;: &amp;quot;changeme&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| active || N || Whether the user is enabled. Defaults to the existing active state.&lt;br /&gt;
|- &lt;br /&gt;
| roles || N || Zero or more roles as defined in [[Unified_API#Enumerations|Enumerations]]. Supplying an empty list of roles indicates it is a normal read-only user. Supplying null indicates that the roles should not be updated.&lt;br /&gt;
|- &lt;br /&gt;
| password || N || A password at least 6 characters long. If not supplied, the password is not changed.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as POST.&lt;br /&gt;
&lt;br /&gt;
==DELETE an existing user ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;DELETE http(s)://localhost/rest/users/{userId}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.user.User&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Deletes a user. Returns a 404 if not found.&lt;br /&gt;
&lt;br /&gt;
Responses have the same format as GET, and return the final state of the user before the delete.&lt;br /&gt;
&lt;br /&gt;
==GET a list of known MFA users==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;As of version 5.0.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/users/mfa&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.user.MfaUserListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Returns all known users which may have Multi-Factor Authentication (MFA) resets applied. Exists to facilitate use of the MFA reset endpoint.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;userNames&amp;quot;: [&lt;br /&gt;
    &amp;quot;admin&amp;quot;,&lt;br /&gt;
    &amp;quot;john.doe&amp;quot;,&lt;br /&gt;
    &amp;quot;operator&amp;quot;&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==PUT MFA user resets==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;As of version 5.0.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;PUT http(s)://localhost/rest/users/mfa/reset&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.user.ResetMfaRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.user.MfaUserListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Resets the requested users&#039; Multi-Factor Authentication (MFA) state so that they may perform MFA setup. This is useful when a user does not complete MFA setup within the required time, or when adding a user when not using Obsidian&#039;s native authentication.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;userNames&amp;quot;: [&lt;br /&gt;
    &amp;quot;admin&amp;quot;,&lt;br /&gt;
    &amp;quot;john.doe&amp;quot;,&lt;br /&gt;
    &amp;quot;operator&amp;quot;&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| userNames || Y || The list of user names to reset. Note that these do not have to correspond to known Obsidian users, since it must support custom authentication methods.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Responses echo the users in the request.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;userNames&amp;quot;: [&lt;br /&gt;
    &amp;quot;admin&amp;quot;,&lt;br /&gt;
    &amp;quot;john.doe&amp;quot;,&lt;br /&gt;
    &amp;quot;operator&amp;quot;&lt;br /&gt;
  ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= System Restore Endpoints =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
These endpoinds can be used to import and export Obsidian&#039;s configuration, including job-related configuration, subscription-related configuration, users and system parameters. See [[Initializing_and_Restoring|Initializing and Restoring]] for more details.&lt;br /&gt;
&lt;br /&gt;
==GET a system restore configuration ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/system_restores[?excludeItem=users&amp;amp;jobNickname=jobname]&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.system.restore.SystemRestoreConfiguration&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Returns the system&#039;s exported configuration. The output from this endpoint can be used with the corresponding [[#PUT_a_system_restore_configuration|PUT endpoint]] directly.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Query String Parameters&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|-&lt;br /&gt;
| excludeItem || N || Allows filtering out of specific groups of system configuration items. Supported values are &amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;chains&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;conflicts&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;systemParameters&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;customCalendars&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;globalParameters&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;users&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;templates&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;subscribers&amp;lt;/code&amp;gt;. Filtering out jobs automatically also filters out chains and conflicts and drops any other job references (such as in templates and subscribers). Supports multiple values.&lt;br /&gt;
|-&lt;br /&gt;
| jobNickname || N || Allows targeting only specific jobs in the jobs export. Filtering out any jobs automatically filters out all conflicts, any chains where these jobs exist and and drops any other references to these jobs (such as in templates and subscribers). Supports multiple values.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response (with inline comments)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;jobs&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;updateSchedule&amp;quot;: false, // whether to apply the current schedule and any additional jobSchedules when the job already exists&lt;br /&gt;
         &amp;quot;updateAttributes&amp;quot;: false, // whether job attributes (jobClass, recoveryType, pickupBufferMinutes, etc.) will be updated when the job exists&lt;br /&gt;
         &amp;quot;jobSchedules&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
               &amp;quot;state&amp;quot;: &amp;quot;DISABLED&amp;quot;,&lt;br /&gt;
               &amp;quot;effectiveDate&amp;quot;: &amp;quot;2015-03-20T10:35:00-0700&amp;quot;,&lt;br /&gt;
               &amp;quot;endDate&amp;quot;: &amp;quot;2999-12-31T23:59:00-0800&amp;quot;&lt;br /&gt;
            }&lt;br /&gt;
         ],&lt;br /&gt;
         &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.LogCleanupJob&amp;quot;,&lt;br /&gt;
         &amp;quot;minExecutionDuration&amp;quot;: &amp;quot;2s&amp;quot;,&lt;br /&gt;
         &amp;quot;maxExecutionDuration&amp;quot;: &amp;quot;3s&amp;quot;,&lt;br /&gt;
         &amp;quot;nickname&amp;quot;: &amp;quot;Log Cleanup&amp;quot;,&lt;br /&gt;
         &amp;quot;folder&amp;quot;: &amp;quot;Production/Test&amp;quot;, // as of 4.1.0&lt;br /&gt;
         &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
         &amp;quot;hosts&amp;quot;: [ &amp;quot;Demo-PC&amp;quot; ],&lt;br /&gt;
         &amp;quot;ordinalParameters&amp;quot;: [&lt;br /&gt;
            &amp;quot;ordinal&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;parameters&amp;quot;: [&lt;br /&gt;
              {&lt;br /&gt;
                 &amp;quot;name&amp;quot;: &amp;quot;level&amp;quot;,&lt;br /&gt;
                 &amp;quot;type&amp;quot;: &amp;quot;STRING&amp;quot;,&lt;br /&gt;
                 &amp;quot;value&amp;quot;: &amp;quot;ALL&amp;quot;&lt;br /&gt;
              },&lt;br /&gt;
              {&lt;br /&gt;
                 &amp;quot;name&amp;quot;: &amp;quot;maxAgeDays&amp;quot;,&lt;br /&gt;
                 &amp;quot;type&amp;quot;: &amp;quot;INTEGER&amp;quot;,&lt;br /&gt;
                 &amp;quot;value&amp;quot;: &amp;quot;120&amp;quot;&lt;br /&gt;
              }&lt;br /&gt;
            ]&lt;br /&gt;
         ],&lt;br /&gt;
         &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
         &amp;quot;hostPreference&amp;quot;: true,&lt;br /&gt;
         &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
         &amp;quot;state&amp;quot;: &amp;quot;ENABLED&amp;quot;,&lt;br /&gt;
         &amp;quot;schedule&amp;quot;: &amp;quot;* * * * *&amp;quot;,&lt;br /&gt;
         &amp;quot;effectiveDate&amp;quot;: &amp;quot;2015-01-01T00:00:00-0700&amp;quot;&lt;br /&gt;
         &amp;quot;endDate&amp;quot;: &amp;quot;2015-03-20T10:34:00-0700&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;updateSchedule&amp;quot;: false,&lt;br /&gt;
         &amp;quot;updateAttributes&amp;quot;: false,&lt;br /&gt;
         &amp;quot;jobSchedules&amp;quot;: [],&lt;br /&gt;
         &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.script.GroovyJob&amp;quot;,&lt;br /&gt;
         &amp;quot;nickname&amp;quot;: &amp;quot;Script Job&amp;quot;,&lt;br /&gt;
         &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
         &amp;quot;hosts&amp;quot;: [],&lt;br /&gt;
         &amp;quot;ordinalParameters&amp;quot;: [&lt;br /&gt;
            &amp;quot;ordinal&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;parameters&amp;quot;: [&lt;br /&gt;
              {&lt;br /&gt;
                 name&amp;quot;: &amp;quot;script&amp;quot;,&lt;br /&gt;
                 &amp;quot;type&amp;quot;: &amp;quot;STRING&amp;quot;,&lt;br /&gt;
                 &amp;quot;value&amp;quot;: &amp;quot;sdfds&amp;quot;&lt;br /&gt;
              }&lt;br /&gt;
            ]&lt;br /&gt;
         ],&lt;br /&gt;
         &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
         &amp;quot;hostPreference&amp;quot;: false,&lt;br /&gt;
         &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
         &amp;quot;state&amp;quot;: &amp;quot;DISABLED&amp;quot;,&lt;br /&gt;
         &amp;quot;effectiveDate&amp;quot;: &amp;quot;2015-01-01T00:00:00-0700&amp;quot;&lt;br /&gt;
         &amp;quot;endDate&amp;quot;: &amp;quot;2999-12-31T23:59:00-0800&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
   ],&lt;br /&gt;
   &amp;quot;users&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;update&amp;quot;: false, // when false, the user attributes will not be updated if the user already exists&lt;br /&gt;
         &amp;quot;userName&amp;quot;: &amp;quot;admin&amp;quot;,&lt;br /&gt;
         &amp;quot;active&amp;quot;: true,&lt;br /&gt;
         &amp;quot;roles&amp;quot;: [&lt;br /&gt;
            &amp;quot;API&amp;quot;,&lt;br /&gt;
            &amp;quot;ADMIN&amp;quot;,&lt;br /&gt;
            &amp;quot;WRITE&amp;quot;&lt;br /&gt;
         ],&lt;br /&gt;
         &amp;quot;password&amp;quot;: &amp;quot;changeme&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
   ],&lt;br /&gt;
   &amp;quot;chains&amp;quot;: {&lt;br /&gt;
      &amp;quot;replaceAll&amp;quot;: false, // if set to false or omitted, chains will only be created if none exist - existing chains are not updated.&lt;br /&gt;
      &amp;quot;items&amp;quot;: [&lt;br /&gt;
         {&lt;br /&gt;
            &amp;quot;sourceJobNickname&amp;quot;: &amp;quot;Script Job&amp;quot;,&lt;br /&gt;
            &amp;quot;targetJobNickname&amp;quot;: &amp;quot;Log Cleanup&amp;quot;,&lt;br /&gt;
            &amp;quot;active&amp;quot;: true,&lt;br /&gt;
            &amp;quot;triggerStates&amp;quot;: [&lt;br /&gt;
               &amp;quot;FAILED&amp;quot;,&lt;br /&gt;
               &amp;quot;CONDITIONAL&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;resultConditions&amp;quot;: [&lt;br /&gt;
               {&lt;br /&gt;
                  &amp;quot;variableName&amp;quot;: &amp;quot;dfdsfds&amp;quot;,&lt;br /&gt;
                  &amp;quot;operator&amp;quot;: &amp;quot;EQUALS&amp;quot;,&lt;br /&gt;
                  &amp;quot;values&amp;quot;: [&lt;br /&gt;
                     &amp;quot;sdfs&amp;quot;&lt;br /&gt;
                  ]&lt;br /&gt;
               }&lt;br /&gt;
            ]&lt;br /&gt;
         }&lt;br /&gt;
      ]&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;conflicts&amp;quot;: [&lt;br /&gt;
      [&lt;br /&gt;
         &amp;quot;Script Job&amp;quot;,&lt;br /&gt;
         &amp;quot;Log Cleanup&amp;quot;&lt;br /&gt;
      ]&lt;br /&gt;
   ],&lt;br /&gt;
   &amp;quot;customCalendars&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;name&amp;quot;: &amp;quot;Sample Calendar&amp;quot;,&lt;br /&gt;
         &amp;quot;dates&amp;quot;: [&lt;br /&gt;
            &amp;quot;2011-01-01&amp;quot;&lt;br /&gt;
         ]&lt;br /&gt;
      }&lt;br /&gt;
   ],&lt;br /&gt;
   &amp;quot;systemParameters&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;name&amp;quot;: &amp;quot;adHocJobsRespectFixedHostsRestrictions&amp;quot;,&lt;br /&gt;
         &amp;quot;description&amp;quot;: &amp;quot;This value determines whether the Fixed Hosts restriction assigned to a Job is respected for Ad Hoc jobs.&amp;quot;,&lt;br /&gt;
         &amp;quot;category&amp;quot;: &amp;quot;JOB&amp;quot;,&lt;br /&gt;
         &amp;quot;value&amp;quot;: &amp;quot;true&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
   ],&lt;br /&gt;
   &amp;quot;templates&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;jobNicknames&amp;quot;: [],&lt;br /&gt;
         &amp;quot;name&amp;quot;: &amp;quot;Obsidian Default Job Template&amp;quot;,&lt;br /&gt;
         &amp;quot;active&amp;quot;: true,&lt;br /&gt;
         &amp;quot;category&amp;quot;: &amp;quot;JOB&amp;quot;,&lt;br /&gt;
         &amp;quot;defaultForJobs&amp;quot;: true,&lt;br /&gt;
         &amp;quot;subjectTemplate&amp;quot;: &amp;quot;Obsidian [{{hostName}}] {{subject}}&amp;quot;,&lt;br /&gt;
         &amp;quot;bodyTemplate&amp;quot;: &amp;quot;Body Template&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
   ],&lt;br /&gt;
   &amp;quot;subscribers&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;generalSubscriptions&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
               &amp;quot;jobNickname&amp;quot;: &amp;quot;Log Cleanup&amp;quot;,&lt;br /&gt;
               &amp;quot;category&amp;quot;: &amp;quot;JOB&amp;quot;,&lt;br /&gt;
               &amp;quot;level&amp;quot;: &amp;quot;ERROR&amp;quot;,&lt;br /&gt;
               &amp;quot;active&amp;quot;: true&lt;br /&gt;
            }&lt;br /&gt;
         ],&lt;br /&gt;
         &amp;quot;jobExecutionSubscriptions&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
               &amp;quot;jobNicknames&amp;quot;: [],&lt;br /&gt;
               &amp;quot;allJobs&amp;quot;: true,&lt;br /&gt;
               &amp;quot;triggerStates&amp;quot;: [&lt;br /&gt;
                  &amp;quot;CONDITIONAL&amp;quot;,&lt;br /&gt;
                  &amp;quot;DIED&amp;quot;,&lt;br /&gt;
                  &amp;quot;FAILED&amp;quot;,&lt;br /&gt;
                  &amp;quot;RECOVERY&amp;quot;&lt;br /&gt;
               ],&lt;br /&gt;
               &amp;quot;resultConditions&amp;quot;: [&lt;br /&gt;
                  {&lt;br /&gt;
                     &amp;quot;variableName&amp;quot;: &amp;quot;someVar&amp;quot;,&lt;br /&gt;
                     &amp;quot;operator&amp;quot;: &amp;quot;EQUALS&amp;quot;,&lt;br /&gt;
                     &amp;quot;values&amp;quot;: [&lt;br /&gt;
                        &amp;quot;someValue&amp;quot;&lt;br /&gt;
                     ]&lt;br /&gt;
                  }&lt;br /&gt;
               ],&lt;br /&gt;
               &amp;quot;active&amp;quot;: true&lt;br /&gt;
            }&lt;br /&gt;
         ],&lt;br /&gt;
         &amp;quot;emailAddress&amp;quot;: &amp;quot;test@example.com&amp;quot;,&lt;br /&gt;
         &amp;quot;active&amp;quot;: true&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==PUT a system restore configuration==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;PUT http(s)://localhost/rest/system_restores&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.system.restore.SystemRestoreConfiguration&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.system.restore.SystemRestoreConfiguration&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Imports the requested system restore configuration, updating, creating and replacing data based on the input.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Usage Note:&#039;&#039;&#039; Entities within the export are generally identified by their names. For details on required fields, formats and update logic, refer to the [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/SystemRestoreManager.html#updateConfiguration(com.carfey.ops.api.bean.system.restore.SystemRestoreConfiguration,java.lang.String) Javadoc] and the [[Initializing_and_Restoring|Initializing and Restoring]] page. The Embedded API and REST API both use the same format and processing rules.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Requests are in the same format as the [[#GET_a_system_restore_configuration|GET endpoint&#039;s]] response.&lt;br /&gt;
&lt;br /&gt;
Responses are in the same format as the GET and return the full Obsidian system restore configuration following the changes (not necessarily the same as the input).&lt;br /&gt;
&lt;br /&gt;
= Schedule Alias  Endpoints =&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;As of version 5.0.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
These endpoints can be used to manage schedule aliases. See [[Admin_Schedule_Aliases|Schedule Aliases]] for more details.&lt;br /&gt;
&lt;br /&gt;
==GET a list of schedule aliases ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/schedule_aliases&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.schedule.ScheduleAliasListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Returns the system&#039;s schedule aliases.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;scheduleAliases&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;alias&amp;quot;: &amp;quot;@every4Minutes&amp;quot;,&lt;br /&gt;
            &amp;quot;schedule&amp;quot;: &amp;quot;0/4 * * * *&amp;quot;,&lt;br /&gt;
            &amp;quot;scheduleDescription&amp;quot;: &amp;quot;Every 4th minute every day&amp;quot; (as of Obsidian 5.2.0)&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;alias&amp;quot;: &amp;quot;@dailyAtNoon&amp;quot;,&lt;br /&gt;
            &amp;quot;schedule&amp;quot;: &amp;quot;12 * * * *&amp;quot;,&lt;br /&gt;
            &amp;quot;scheduleDescription&amp;quot;: &amp;quot;At 12:00PM every day&amp;quot; (as of Obsidian 5.2.0)&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;alias&amp;quot;: &amp;quot;@threeTimesWeekly&amp;quot;,&lt;br /&gt;
            &amp;quot;schedule&amp;quot;: &amp;quot;0 0 * 1,3,5 *&amp;quot;,&lt;br /&gt;
            &amp;quot;scheduleDescription&amp;quot;: &amp;quot;At midnight on Mondays, Wednesdays, Fridays&amp;quot; (as of Obsidian 5.2.0)&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;alias&amp;quot;: &amp;quot;@combinedExample&amp;quot;,&lt;br /&gt;
            &amp;quot;schedule&amp;quot;: &amp;quot;@dailyAtNoon;@threeTimesWeekly&amp;quot;,&lt;br /&gt;
            &amp;quot;scheduleDescription&amp;quot;: &amp;quot;At 12:00PM every day - AND - At midnight on Mondays, Wednesdays, Fridays&amp;quot; (as of Obsidian 5.2.0)&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==PUT a schedule alias==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;PUT http(s)://localhost/rest/schedule_aliases/{alias}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Request bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.schedule.ScheduleAliasUpdateRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.schedule.ScheduleAlias&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Request&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;schedule&amp;quot;: &amp;quot;5 11 * * 2&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{	&amp;quot;alias&amp;quot;: &amp;quot;@1105AMTuesdays&amp;quot;,&lt;br /&gt;
	&amp;quot;schedule&amp;quot;: &amp;quot;5 11 * * 2&amp;quot;,&lt;br /&gt;
	&amp;quot;schedule&amp;quot;: &amp;quot;At 11:05AM on Tuesdays&amp;quot; (as of Obsidian 5.2.0)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==GET a schedule alias==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/schedule_aliases/{alias}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.schedule.ScheduleAlias&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{	&amp;quot;alias&amp;quot;: &amp;quot;@1105AMTuesdays&amp;quot;,&lt;br /&gt;
	&amp;quot;schedule&amp;quot;: &amp;quot;5 11 * * 2&amp;quot;,&lt;br /&gt;
	&amp;quot;schedule&amp;quot;: &amp;quot;At 11:05AM on Tuesdays&amp;quot; (as of Obsidian 5.2.0)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==DELETE a schedule alias==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;DELETE http(s)://localhost/rest/schedule_aliases/{alias}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.schedule.ScheduleAlias&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{	&amp;quot;alias&amp;quot;: &amp;quot;@1105AMTuesdays&amp;quot;,&lt;br /&gt;
	&amp;quot;schedule&amp;quot;: &amp;quot;5 11 * * 2&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Job Execution Statistics Endpoints =&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;As of version 6.4.0.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
To retrieve job execution statistics. Assumes [[Built-in_Jobs#Obsidian_Execution_Statistics_Job|Execution Statistics Job]] has run to completion at least once.&lt;br /&gt;
&lt;br /&gt;
== GET a list of job execution statistics ==&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;GET http(s)://localhost/rest/stats[?jobNickname=jobname&amp;amp;job_id=1&amp;amp;duration=six_months&amp;amp;unit=seconds&amp;amp;status=completed&amp;amp;acrossHosts=false&amp;amp;host=prod.obsidian-a1&amp;amp;host=prod.obsidian-a2]&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Query String Parameters&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|-&lt;br /&gt;
| jobNickname || N || Allows targeting only specific jobs by nickname in the stats export. Supports multiple values.&lt;br /&gt;
|-&lt;br /&gt;
| jobId || N || Allows targeting only specific jobs by id in the stats export. Supports multiple values.&lt;br /&gt;
|-&lt;br /&gt;
| duration || N || Allows targeting stats durations. See [[Unified_API#Enumerations|Enumerations]] for valid values - case-insensitive. Supports multiple values.&lt;br /&gt;
|-&lt;br /&gt;
| unit || N || Allows targeting stats units. See [[Unified_API#Enumerations|Enumerations]] for valid values - case-insensitive. Supports multiple values.&lt;br /&gt;
|-&lt;br /&gt;
| status || N || Allows targeting stats terminal statuses. See [[Unified_API#Enumerations|Enumerations]] for valid values - case-insensitive. Supports multiple values.&lt;br /&gt;
|-&lt;br /&gt;
| host || N || Allows targeting stats from specific hosts. Supports multiple values.&lt;br /&gt;
|-&lt;br /&gt;
| acrossHosts || N || Allows selecting stats collected across all hosts. Boolean.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Response bean class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.api.bean.stats.StatsListing&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Returns the cluster&#039;s job execution statistics.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Response&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;stats&amp;quot;: [&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;statsId&amp;quot;: 733,&lt;br /&gt;
			&amp;quot;jobId&amp;quot;: 55,&lt;br /&gt;
			&amp;quot;nickname&amp;quot;: &amp;quot;Cache Reloader&amp;quot;,&lt;br /&gt;
			&amp;quot;host&amp;quot;: &amp;quot;prod.obsidian-a1&amp;quot;,&lt;br /&gt;
			&amp;quot;status&amp;quot;: &amp;quot;COMPLETED&amp;quot;,&lt;br /&gt;
			&amp;quot;unit&amp;quot;: &amp;quot;SECONDS&amp;quot;,&lt;br /&gt;
			&amp;quot;duration&amp;quot;: &amp;quot;YEAR&amp;quot;,&lt;br /&gt;
			&amp;quot;start&amp;quot;: &amp;quot;2024-12-14&amp;quot;,&lt;br /&gt;
			&amp;quot;end&amp;quot;: &amp;quot;2025-12-14&amp;quot;,&lt;br /&gt;
			&amp;quot;count&amp;quot;: 97,&lt;br /&gt;
			&amp;quot;average&amp;quot;: 7701.463918,&lt;br /&gt;
			&amp;quot;median&amp;quot;: 6965.0,&lt;br /&gt;
			&amp;quot;mode&amp;quot;: 62,&lt;br /&gt;
			&amp;quot;min&amp;quot;: 62.0,&lt;br /&gt;
			&amp;quot;max&amp;quot;: 17925.0,&lt;br /&gt;
			&amp;quot;variance&amp;quot;: 23055960.248698,&lt;br /&gt;
			&amp;quot;standardDeviation&amp;quot;: 4801.66&lt;br /&gt;
		},&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;statsId&amp;quot;: 545,&lt;br /&gt;
			&amp;quot;jobId&amp;quot;: 55,&lt;br /&gt;
			&amp;quot;nickname&amp;quot;: &amp;quot;Cache Reloader&amp;quot;,&lt;br /&gt;
			&amp;quot;host&amp;quot;: &amp;quot;prod.obsidian-a2&amp;quot;,&lt;br /&gt;
			&amp;quot;status&amp;quot;: &amp;quot;COMPLETED&amp;quot;,&lt;br /&gt;
			&amp;quot;unit&amp;quot;: &amp;quot;HOURS&amp;quot;,&lt;br /&gt;
			&amp;quot;duration&amp;quot;: &amp;quot;YEAR&amp;quot;,&lt;br /&gt;
			&amp;quot;start&amp;quot;: &amp;quot;2024-12-14&amp;quot;,&lt;br /&gt;
			&amp;quot;end&amp;quot;: &amp;quot;2025-12-14&amp;quot;,&lt;br /&gt;
			&amp;quot;count&amp;quot;: 114,&lt;br /&gt;
			&amp;quot;average&amp;quot;: 2.238684,&lt;br /&gt;
			&amp;quot;median&amp;quot;: 1.932778,&lt;br /&gt;
			&amp;quot;mode&amp;quot;: 1,&lt;br /&gt;
			&amp;quot;min&amp;quot;: 0.015,&lt;br /&gt;
			&amp;quot;max&amp;quot;: 4.989444,&lt;br /&gt;
			&amp;quot;variance&amp;quot;: 2.18319,&lt;br /&gt;
			&amp;quot;standardDeviation&amp;quot;: 1.47756&lt;br /&gt;
		}&lt;br /&gt;
	],&lt;br /&gt;
	&amp;quot;hosts&amp;quot;: [&lt;br /&gt;
		&amp;quot;prod.obsidian-a1&amp;quot;,&lt;br /&gt;
		&amp;quot;prod.obsidian-a2&amp;quot;&lt;br /&gt;
	]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=REST_API&amp;diff=4156</id>
		<title>REST API</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=REST_API&amp;diff=4156"/>
		<updated>2026-07-31T14:49:57Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian includes a REST API to ease integration into other applications and software environments, regardless of the technology used. A complete range of job, scheduling and host management features are exposed via the API. It is exposed by both the standalone and bundled versions of the admin web application.&lt;br /&gt;
&lt;br /&gt;
This page contains general information on the REST API, including data formats, authentication and common API behaviour.&lt;br /&gt;
&lt;br /&gt;
For full details specific API endpoints, see [[REST Endpoints|Endpoints]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Obsidian also has an [[Embedded API]], which can be used when no web application is available or you&#039;d simply rather not use REST.&lt;br /&gt;
&lt;br /&gt;
=Overview=&lt;br /&gt;
&lt;br /&gt;
The Obsidian REST API allows other applications a way to access Obsidian data, schedule jobs, and control Obsidian in a variety of ways. It can be integrated with any programming language since it uses standards like HTTP and JSON. &lt;br /&gt;
&lt;br /&gt;
The REST API gives users the power to do things like automate migration scripts, trigger jobs based on events in external applications, and expose pieces of the Obsidian API in their own custom user interfaces.&lt;br /&gt;
&lt;br /&gt;
==Endpoints==&lt;br /&gt;
The API is exposed under the path &amp;lt;code&amp;gt;/rest&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
For example, if your web application is deployed at &amp;lt;code&amp;gt;localhost&amp;lt;/code&amp;gt;, the jobs endpoint is accessible at &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;http://localhost/rest/jobs&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
For full details on the API endpoints, see [[REST Endpoints|Endpoints]].&lt;br /&gt;
&lt;br /&gt;
==Data Format==&lt;br /&gt;
[http://www.json.org JSON] is used for all endpoints. This includes response data and PUT/POST data.&lt;br /&gt;
&lt;br /&gt;
The returned &amp;lt;code&amp;gt;Content-Type&amp;lt;/code&amp;gt; will always be &amp;lt;code&amp;gt;application/json&amp;lt;/code&amp;gt; using the UTF-8 encoding.&lt;br /&gt;
&lt;br /&gt;
All PUTs and POSTs must include &amp;lt;code&amp;gt;Content-Type&amp;lt;/code&amp;gt; header set to &amp;lt;code&amp;gt;application/json&amp;lt;/code&amp;gt; with a valid JSON body. UTF-8 should be used as the content encoding for maximum compatibility.&lt;br /&gt;
&lt;br /&gt;
==Authentication &amp;amp; Authorization==&lt;br /&gt;
The REST API is secured by limiting access to users configured in [[Admin User Management|User Management]] with the API Access role.&lt;br /&gt;
&lt;br /&gt;
Valid [http://en.wikipedia.org/wiki/Basic_access_authentication basic access authentication] must be supplied with every request. Valid API users are authorized to access any REST endpoint.&lt;br /&gt;
&lt;br /&gt;
=== Disabling Authentication ===&lt;br /&gt;
In some rare cases, users may wish to disable the need for authentication credentials in REST calls.&lt;br /&gt;
&lt;br /&gt;
This can be done by changing the [[Admin Scheduler Settings|scheduler setting]] &amp;lt;code&amp;gt;allowAnonymousRestCalls&amp;lt;/code&amp;gt; under the &#039;&#039;Dispatch&#039;&#039; category to &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;. When this is done, no Authentication header needs to be provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; This is not recommended on networks exposed to the Internet, or other non-trusted environments.&lt;br /&gt;
&lt;br /&gt;
=Common API Behaviour=&lt;br /&gt;
* For GET and DELETE, parameterization is done via request parameters, and not JSON body. Supported parameters are simple and primarily provide search options and simple flags.&lt;br /&gt;
* All non-200 responses will always return a JSON response in the following format. One or more errors may be returned in the errors property.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{ &lt;br /&gt;
    &amp;quot;errors&amp;quot;:[&amp;quot;nickname is required.&amp;quot;, &amp;quot;jobClass is required.&amp;quot;]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{ &lt;br /&gt;
    &amp;quot;errors&amp;quot;:[&amp;quot;Resource not found&amp;quot;]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* If a request does not pass validation or if an action cannot be performed, a 400 status code will be returned. This may happen if required fields are omitted, in an invalid format, or an action cannot be performed in the current context.&lt;br /&gt;
* If a JSON payload contains unexpected fields, a 400 status code will be returned. &lt;br /&gt;
* If a resource with the specified identifier could not be found, a 404 status code will be returned. For example,  this would occur if you attempt to load a job with an ID that does not exist.&lt;br /&gt;
* If an incorrect method is used on an endpoint (e.g. DELETE on a job runtime), a 405 status code will be returned.&lt;br /&gt;
* If no valid basic authentication is provided, a 401 status code will be returned.&lt;br /&gt;
* If no endpoint exists at the requested path, a 404 status code will be returned.&lt;br /&gt;
* If the Content-Type header of a PUT or POST request is not application/json, a 415 status code will be returned. &lt;br /&gt;
* If the server encounters an unexpected error, a 500 status code will be returned.&lt;br /&gt;
&lt;br /&gt;
== Date Formats ==&lt;br /&gt;
Since scheduling is inherently linked to time zones and is not fully represented by a UTC time, all times in the API are returned in the following string format. Note the trailing timezone indicator which indicates the UTC offset.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
yyyy-MM-dd&#039;T&#039;HH:mm:ssZ&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For example, the last second of 2012 PST is:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2012-12-31T23:59:59-0800&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a date only is input or output (as with custom calendars), it is always in the following format:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2012-12-31&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Date &amp;amp; Time Inputs ===&lt;br /&gt;
While datetimes are always output in the same format, datetimes in query strings or in JSON payloads can use either the standard output format shown above, an abbreviated form that contains no timezone offset and assumes server time, or the UTC time represented as milliseconds since the epoch.&lt;br /&gt;
For example, the last second of 2012 PST is:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2012-12-31T23:59:59-0800&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Or, interpreted in the server time zone:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2012-12-31T23:59:59&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Or, as milliseconds since the epoch:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1356987599000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Common Enumerations=&lt;br /&gt;
&lt;br /&gt;
Enumerations are consistent with the [[Unified_API#Enumerations|Embedded API&#039;s enumerations]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= JSON Bean Classes =&lt;br /&gt;
&lt;br /&gt;
Obsidian comes bundled with simple bean classes in the package &amp;lt;code&amp;gt;com.carfey.ops.api.bean&amp;lt;/code&amp;gt; that can be used to serialize JSON requests and deserialize responses into plain old Java objects (POJOs). These bean classes are located in &amp;lt;code&amp;gt;obsidian.jar&amp;lt;/code&amp;gt; which is included in your Obsidian zip download within the &amp;lt;code&amp;gt;standalone&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
These classes are tested with [https://github.com/google/gson Gson] since that is what Obsidian uses internally, but you can integrate it with the JSON library of your choosing. Obsidian uses custom types for dates and datetimes, so see the following section on how to configure JSON serialization correctly.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Each [[REST Endpoints|endpoint]] documents the appropriate bean class to use.&lt;br /&gt;
&lt;br /&gt;
== Serializing &amp;amp; Deserializing Date Types ==&lt;br /&gt;
&lt;br /&gt;
Since the Obsidian REST API uses its own specific date formats to fully represent scheduled times including their time zones, you will have to use custom serializers to properly use the supplied bean classes. Doing this is simple, and we&#039;ve provided a sample below for [https://github.com/google/gson Gson], plus a generic example to show you how to parse and format dates appropriately, which you can adapt for use in JSON other libraries.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;GSON Serialization:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
import com.carfey.jdk.lang.Date;&lt;br /&gt;
import com.carfey.jdk.lang.DateTime;&lt;br /&gt;
&lt;br /&gt;
import com.carfey.jdk.lang.Date;&lt;br /&gt;
import com.carfey.jdk.lang.DateFormat.ParseException;&lt;br /&gt;
import com.carfey.jdk.lang.DateTime;&lt;br /&gt;
import com.carfey.jdk.text.Json;&lt;br /&gt;
import com.google.gson.Gson;&lt;br /&gt;
import com.google.gson.GsonBuilder;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
public class GsonDemo {&lt;br /&gt;
	public static void main(String[] args) throws ParseException {&lt;br /&gt;
&lt;br /&gt;
        // Set up the custom serializers for date types&lt;br /&gt;
        GsonBuilder gb = new GsonBuilder();&lt;br /&gt;
        gb.registerTypeAdapter(Date.class, Json.REST_SERIALIZER);&lt;br /&gt;
        gb.registerTypeAdapter(DateTime.class, Json.REST_SERIALIZER);&lt;br /&gt;
        Gson gson = gb.create();&lt;br /&gt;
&lt;br /&gt;
        // The Gson instance can now be used with all Obsidian REST bean classes&lt;br /&gt;
        SampleBean sample = new SampleBean();&lt;br /&gt;
        String stringValue = gson.toJson(sample);&lt;br /&gt;
        System.out.println(stringValue);&lt;br /&gt;
&lt;br /&gt;
        SampleBean rebuilt = gson.fromJson(stringValue, SampleBean.class);&lt;br /&gt;
        System.out.println(rebuilt);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private static class SampleBean {&lt;br /&gt;
        private com.carfey.jdk.lang.Date date = new Date();&lt;br /&gt;
        private com.carfey.jdk.lang.DateTime dateTime = new DateTime();&lt;br /&gt;
        public Date getDate() {&lt;br /&gt;
            return date;&lt;br /&gt;
        }&lt;br /&gt;
        public void setDate(Date date) {&lt;br /&gt;
            this.date = date;&lt;br /&gt;
        }&lt;br /&gt;
        public DateTime getDateTime() {&lt;br /&gt;
            return dateTime;&lt;br /&gt;
        }&lt;br /&gt;
        public void setDateTime(DateTime dateTime) {&lt;br /&gt;
            this.dateTime = dateTime;&lt;br /&gt;
        }&lt;br /&gt;
        @Override&lt;br /&gt;
        public String toString() {&lt;br /&gt;
            return &amp;quot;SampleBean [date=&amp;quot; + date + &amp;quot;, dateTime=&amp;quot; + dateTime + &amp;quot;]&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Output:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&amp;quot;date&amp;quot;:&amp;quot;2013-10-30&amp;quot;,&amp;quot;dateTime&amp;quot;:&amp;quot;2013-10-30T22:04:24-0700&amp;quot;}&lt;br /&gt;
SampleBean [date=20131030, dateTime=20131030 10:04:24:000 PM -0700]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Generic Date Parsing and Formatting Example:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// converting REST date time formats (serialize and deserialize)&lt;br /&gt;
com.carfey.jdk.lang.DateFormat dateTimeFormat = new com.carfey.jdk.lang.DateFormat(&amp;quot;yyyy-MM-dd&#039;T&#039;HH:mm:ssZ&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
com.carfey.jdk.lang.DateTime dateTime = new com.carfey.jdk.lang.DateTime();&lt;br /&gt;
String dateTimeString = dateTimeFormat.format(dateTime);&lt;br /&gt;
com.carfey.jdk.lang.DateTime asDateTime = dateTimeFormat.parse(dateTimeString);&lt;br /&gt;
System.out.println(asDateTime);&lt;br /&gt;
&lt;br /&gt;
com.carfey.jdk.lang.DateFormat dateFormat = new com.carfey.jdk.lang.DateFormat(&amp;quot;yyyy-MM-dd&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
com.carfey.jdk.lang.Date date = new com.carfey.jdk.lang.Date();&lt;br /&gt;
String dateString = dateTimeFormat.format(date);&lt;br /&gt;
com.carfey.jdk.lang.Date asDate = dateTimeFormat.parse(dateString).getDate();&lt;br /&gt;
System.out.println(asDate);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Output: &#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
20131105 06:10:41:000 PM -0800&lt;br /&gt;
20131105&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Obsidian_Tables&amp;diff=4155</id>
		<title>Obsidian Tables</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Obsidian_Tables&amp;diff=4155"/>
		<updated>2026-07-31T14:49:57Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian needs to create and manage the following tables in its target database. When sharing schemas between Obsidian and other applications, ensure there are no naming conflicts by reviewing this list.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Since Obsidian does not run jobs within the same connection or transaction as your jobs, there is no real need to run Obsidian within the same database or schema as your own application(s), though you may wish to do so to facilitate maintenance.&lt;br /&gt;
&lt;br /&gt;
Obsidian can be configured to use a [[Advanced_Configuration#Table Prefixes|table prefix]] when creating its tables. &lt;br /&gt;
&lt;br /&gt;
* custom_calendar&lt;br /&gt;
* event_log&lt;br /&gt;
* event_subscriber&lt;br /&gt;
* event_subscription&lt;br /&gt;
* global_job_config&lt;br /&gt;
* job&lt;br /&gt;
* job_chain&lt;br /&gt;
* job_chain_mode&lt;br /&gt;
* job_chain_mode_condition&lt;br /&gt;
* job_configuration&lt;br /&gt;
* job_conflict_config&lt;br /&gt;
* job_history&lt;br /&gt;
* job_history_config&lt;br /&gt;
* job_history_chain&lt;br /&gt;
* job_history_error&lt;br /&gt;
* job_history_interrupt&lt;br /&gt;
* job_history_result&lt;br /&gt;
* job_running_host&lt;br /&gt;
* job_sub_mode_condition&lt;br /&gt;
* job_subscription&lt;br /&gt;
* job_subscription_job&lt;br /&gt;
* job_subscription_mode&lt;br /&gt;
* job_state&lt;br /&gt;
* notification&lt;br /&gt;
* notif_template&lt;br /&gt;
* notif_template_entity&lt;br /&gt;
* operations_parameter&lt;br /&gt;
* role&lt;br /&gt;
* semaphore&lt;br /&gt;
* sequence_manager&lt;br /&gt;
* suite_user&lt;br /&gt;
* user_cookie&lt;br /&gt;
* user_role&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Oracle Privileges ==&lt;br /&gt;
&lt;br /&gt;
If you are not using the schema owner as your Obsidian database user, the following privileges will need to be granted to your database user:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# For each Obsidian table...&lt;br /&gt;
GRANT INSERT,SELECT,UPDATE,DELETE ON OBS_JOB TO OBSIDIAN_USER;&lt;br /&gt;
&lt;br /&gt;
# Required for alternate schema usage&lt;br /&gt;
GRANT ALTER SESSION TO OBSIDIAN_USER;&lt;br /&gt;
GRANT ANALYZE ANY TO OBSIDIAN_USER;&lt;br /&gt;
GRANT ANALYZE ANY DICTIONARY TO OBSIDIAN_USER;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== MS SQL Server Snapshot Isolation ==&lt;br /&gt;
&lt;br /&gt;
For maximum compatibility and to avoid deadlocks, MS SQL Server should be configured to use [https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql/snapshot-isolation-in-sql-server read committed snapshot isolation].&lt;br /&gt;
&lt;br /&gt;
This can be enabled on your database by running the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ALTER DATABASE MyDatabase SET ALLOW_SNAPSHOT_ISOLATION ON&lt;br /&gt;
ALTER DATABASE MyDatabase SET READ_COMMITTED_SNAPSHOT ON&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Licenses_%26_Nodes&amp;diff=4154</id>
		<title>Licenses &amp; Nodes</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Licenses_%26_Nodes&amp;diff=4154"/>
		<updated>2026-07-31T14:49:56Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Each running Obsidian scheduler process requires a valid license. Here are some quick facts:&lt;br /&gt;
* When you download Obsidian, it automatically obtains a free license key, which gives you one free license for permanent use. We&#039;d be happy to have you evaluate Obsidian clustering, so just reach out to us and we&#039;ll grant you a second instance on the same key for a few months. You can use a single scheduler instance forever at no charge.&lt;br /&gt;
* Running the admin web app without scheduler does not require a license when used in conjunction with licensed instances.&lt;br /&gt;
* All standard licenses are &#039;&#039;floating&#039;&#039;, meaning they are not restricted to particular hardware and can be moved around as needed.&lt;br /&gt;
* Running scheduler instances must have access to the Internet to lease and verify the license. &lt;br /&gt;
* Every license key has one or more valid licenses associated with it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= License Verification =&lt;br /&gt;
The default Obsidian license requires a a live Internet connection to validate clustered scheduler instances.&lt;br /&gt;
&lt;br /&gt;
Obsidian&#039;s license verification is built to handle sporadic outages to Internet connectivity. Customers using paid licenses can generally expect grace periods of over 48 hours during which Internet connectivity is not available. Obsidian will automatically attempt to re-validate your license key on a schedule based on configurable settings which control [[#License Leasing|License Leasing]]&lt;br /&gt;
&lt;br /&gt;
Obsidian also provides a [[Key_Server_Proxy|license proxy]] should you wish to restrict Internet access to a single server. Obsidian instances can then be directed to the deployed license proxy.&lt;br /&gt;
&lt;br /&gt;
Obsidian also supports hardware licenses. These are tied to deployment hardware and are not floating. Contact [[Contact Carfey Software|Carfey Software]] for further details. &lt;br /&gt;
&lt;br /&gt;
Obsidian can also be run under a site license. When Obsidian is run under a site license, no Internet connectivity and verification is required and a specific maximum number of nodes are permitted within the licensed organization based on prior agreement. Contact [[Contact Carfey Software|Carfey Software]] for further details.&lt;br /&gt;
&lt;br /&gt;
=Nodes=&lt;br /&gt;
Nodes are individual instances of Obsidian scheduler.  Nodes can be running standalone, within the admin web app or embedded in an existing JVM application.  Nodes typically are deployed one per host, but Obsidian supports multiple nodes per host.  Each running host uses the hostname to designate the node running each scheduled instance of a job.  Subsequent instances on the same host will suffix an ordinal to the end of the hostname.  Optionally, you can specify a configuration property (see [[Advanced Configuration]]) or provide a System property (see [[Getting_Started#Setting_Host_Names|Getting Started]]) choosing the host designator you wish to use.&lt;br /&gt;
&lt;br /&gt;
No extra work, special configuration or administration is required to add nodes.  Simply setup the environment, ensure a node license is available and start it up. The same holds true for shutting down.&lt;br /&gt;
&lt;br /&gt;
Nodes can be gracefully brought down out of the pool, allowing them to complete any in-process work while no longer claiming and processing any subsequently scheduled work.  Graceful shutdown in the web embedded scheduler, either in the web admin app or your own web app, requires using the container&#039;s shutdown. Starting and stopping a standalone version of the scheduler uses a listener port to support graceful shutdown.  See the [[Getting Started]] guide for details.&lt;br /&gt;
&lt;br /&gt;
== License Leasing ==&lt;br /&gt;
&lt;br /&gt;
When an Obsidian instance is validated, a license is locked to the leasing node. Other nodes or instances will not be able to use this license until the lease expires. &lt;br /&gt;
After the lease time elapses (configured in [[Admin Scheduler Settings|scheduler settings]] screen, or in the key proxy properties file), it is available for other nodes. In the meantime, if the instance goes down and you attempt to start a node in another location, the license will not be available for lease. If you restart the node in the same location, it will reacquire the same license.&lt;br /&gt;
&lt;br /&gt;
You can customize the leasing settings in the [[Admin Scheduler Settings|scheduler settings]] screen under the &#039;&#039;License&#039;&#039; category. Key settings include &#039;&#039;clientKeyRefreshMinutes&#039;&#039; and &#039;&#039;clientKeyLeaseLengthMultiple&#039;&#039;, which are documented on that page. Changing these settings allows you to find the right balance between tolerance for Internet outages, and the ability to deploy a used Obsidian license to a new installation, affecting the length of time for locked licenses to expire.&lt;br /&gt;
&lt;br /&gt;
== Your License Key ==&lt;br /&gt;
&lt;br /&gt;
You can find your license key in [[Admin Scheduler Settings|Scheduler Settings]] as shown below. &lt;br /&gt;
&lt;br /&gt;
Obsidian will automatically acquire your free single node license. You can request a second clustering node for evaluation for up to 1 year. If you are converting your trial backup node license to a paid license or are purchasing additional nodes, you can obtain your license key from the location show below.&lt;br /&gt;
&lt;br /&gt;
[[Image:ClientKey_4.0.png]]&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Key_Server_Proxy&amp;diff=4153</id>
		<title>Key Server Proxy</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Key_Server_Proxy&amp;diff=4153"/>
		<updated>2026-07-31T14:49:56Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian licensing requires access to Carfey Software&#039;s servers to verify license authenticity.  To accommodate those who wish to limit Obsidian hosts with access to the internet, we provide a key server proxy that can be deployed in any servlet container.  &lt;br /&gt;
&lt;br /&gt;
In your software package, you would have received the key server proxy as &amp;lt;code&amp;gt;obsidianKeyProxy.war&amp;lt;/code&amp;gt;. Within this war, under &amp;lt;code&amp;gt;WEB-INF/classes&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt;. Note you should not need to edit the master key server url, which is fixed at &amp;quot;https://licence.carfey.com/proxy&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Once the proxy is deployed, to configure use of the key server proxy by the Obsidian scheduler, simply login to Obsidian Web Admin app as an admin user, open [[Admin_Scheduler_Settings|Scheduler Settings]], select the &#039;&#039;Licence&#039;&#039; category and set the address of your deployed key server proxy as the &#039;&#039;clientKeyServerUrl&#039;&#039;.  Make sure it ends with &amp;quot;/licence&amp;quot;, e.g. &amp;quot;https://internal.server/licence&amp;quot;.  &lt;br /&gt;
&lt;br /&gt;
Then make sure the value for the [[Admin_Scheduler_Settings|scheduler setting]] &#039;&#039;clientKeyServerUrlBackup&#039;&#039; is set to Carfey Software&#039;s key license server at https://licence.carfey.com/licence. This will give you failover in case your proxy goes down.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Security Note:&#039;&#039;&#039; By default, the proxy key server allows non-secure connections. If you wish to force secure connections through HTTPS, you can edit the web.xml in your war and uncomment the &amp;lt;security-constraint&amp;gt; element in the file. This will force all requests to redirect to an encrypted connection. For details on setting up SSL on your servlet container, refer to its documentation.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Job_Forking&amp;diff=4152</id>
		<title>Job Forking</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Job_Forking&amp;diff=4152"/>
		<updated>2026-07-31T14:49:55Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian can run each job in its own JVM. This allows for a number of possibilities, including support for dynamic changes to your deployed jobs (i.e. hot-swapping of JARs). In theory, you could even specialize classpaths on a per job basis by customizing the provided execution scripts.&lt;br /&gt;
&lt;br /&gt;
JVM job forking is disabled by default. Standalone deployments support forking out-of-the-box, but it is not active until you enable it as defined in [[#Configuring_Job_Forking|Configuring Job Forking]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; In order to support hot-swapping of JARs that contain compiled jobs, Obsidian expects you to ensure JAR consistency between nodes. If you plan on deploying new jobs or changing the configurability (e.g. supported parameter changes), you will want to deploy these to all Obsidian nodes that have job forking enabled and either restrict these from executing on nodes with forking disabled (see [[Admin_Jobs#Advanced_Options|Fixed Hosts]]) or deploy the changes to these nodes with appropriate restarts of Obsidian.&lt;br /&gt;
&lt;br /&gt;
= How Does Job Forking Work? =&lt;br /&gt;
When job forking is enabled, Obsidian will run a shell script (or batch file on Windows) for each job execution to be forked. This script in turn starts a new JVM which invokes an Obsidian class with the appropriate arguments to start the single-job execution. Forking can be [[#Script_Customization|customized]] easily by using alternate scripts from the provided defaults. To support new jobs or configurability changes to existing jobs, [[Implementing_Jobs#Classpath_Scanning|Classpath Scanning]] also runs as a forked process using the same scripts.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Forked Jobs and the Obsidian Database&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Each occurrence of a Forked Job will require a few connections to the database. Make sure you plan accordingly in your connection allocations on the database and sizing for the JNDI datasource, if applicable.&lt;br /&gt;
&lt;br /&gt;
== Interrupt Support ==&lt;br /&gt;
&lt;br /&gt;
Forked jobs can be interrupted. See [[Job_Forking#Configuring_Job_Forking | Configuring Job Forking]] below.&lt;br /&gt;
&lt;br /&gt;
= Configuring Job Forking =&lt;br /&gt;
&lt;br /&gt;
=== Enabling Job Forking - Cluster ===&lt;br /&gt;
&lt;br /&gt;
To use forking, first you must enable a setting (&#039;&#039;jvmJobForkingEnabled&#039;&#039;) that will apply across the cluster. This is a [[Admin Scheduler Settings|scheduler setting]] found in the &#039;&#039;Job Spawner&#039;&#039; category. You may enable it through the admin web application, the [[Embedded API]],  [[REST API]], or Obsidian&#039;s [[Initializing and Restoring|initialization &amp;amp; restore]] support.&lt;br /&gt;
&lt;br /&gt;
If you wish to enable interruption of forked jobs that implement the necessary [[Implementing_Jobs#Interruptable_Jobs|Interruptable]] interface, you must also enable the setting &#039;&#039;jvmForkedJobsCanBeInterrupted&#039;&#039; also found in the &#039;&#039;Job Spawner&#039;&#039; category.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:JobForking_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
=== Enabling Job Forking - Node ===&lt;br /&gt;
Once you&#039;ve done that, you&#039;ll need to enable it on each desired node. [[Getting_Started#Standalone_Scheduler|Standalone scheduler]] instances are configured to support forking by default. For other nodes types, you will need to set the appropriate [[Advanced_Configuration#Configuration_Details|property value]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;com.carfey.obsidian.jvmJobForkingEnabledOnThisNode=true&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In a [[Getting_Started#Standalone_Scheduler|standalone scheduler]], job forking will be then be functional on next restart. In other deployments, or if you wish to make additional customizations, additional changes will be required.&lt;br /&gt;
&lt;br /&gt;
=== Script Location ===&lt;br /&gt;
Job forking uses a script for each job execution to start up a fresh JVM.&lt;br /&gt;
&lt;br /&gt;
You may wish to specify the forking script location for other deployments, such as [[Getting_Started#Embedded_Scheduler|embedded schedulers]] and [[Getting_Started#Combined_Scheduler_and_Admin_Web_Application |combined scheduler and admin web application]] instances, or for other reasons.&lt;br /&gt;
&lt;br /&gt;
To override the script location, set the appropriate [[Advanced_Configuration#Configuration_Details|property value]] in your properties file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;com.carfey.obsidian.forkedJobScriptLocation=/Obsidian-3.0.0&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Obsidian is bundled with &#039;&#039;&#039;&amp;lt;code&amp;gt;obsidianForkedJob.bat&amp;lt;/code&amp;gt;&#039;&#039;&#039; and &#039;&#039;&#039;&amp;lt;code&amp;gt;obsidianForkedJob.sh&amp;lt;/code&amp;gt;&#039;&#039;&#039;. These are the &#039;&#039;&#039;required&#039;&#039;&#039; script names.&lt;br /&gt;
If you wish to customize these scripts or use your own, you are free to do so, but the script names cannot be changed. Review the notes on the [[#Forked_Obsidian_Job_Runner|forked job runner]] to assist your customization efforts.&lt;br /&gt;
&lt;br /&gt;
=== Classpath Override ===&lt;br /&gt;
You may wish to override the default classpath that is used by the forked execution instance. Or for deployment types other than [[Getting_Started#Standalone_Scheduler|standalone schedulers]], you can use this [[Advanced_Configuration#Configuration_Details|property]] to specify the classpath.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;com.carfey.obsidian.forkedJobscriptClasspathOverride=/home/user/workspace/obsidian/bin:/home/user/workspace/obsidian/lib/activation-1.1.jar\&lt;br /&gt;
:/home/user/workspace/obsidian/lib/mail-1.4.jar:/home/user/workspace/obsidian/lib/dom4j-1.6.1.jar:/home/user/workspace/obsidian/lib/obsidian.jar\&lt;br /&gt;
:/home/user/workspace/obsidian/lib/log4j-1.2.9.jar:/home/user/workspace/obsidian/lib/gson-2.2.2.jar:/home/user/workspace/obsidian/lib/bsh-2.0b4.jar\&lt;br /&gt;
:/home/user/workspace/obsidian/lib/groovy-all-2.1.8.jar:/home/user/workspace/obsidian/lib/jython-standalone-2.5.3.jar\&lt;br /&gt;
:/home/user/workspace/obsidian/lib/mariadb-java-client-1.1.5.jar&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sample demonstrates usage in an embedded Obsidian instance running inside an Eclipse project. The default classpath used in the &amp;lt;code&amp;gt;obsidianForkedJob.(sh|bat)&amp;lt;/code&amp;gt; script is built automatically assuming a [[Getting_Started#Standalone_Scheduler|standalone scheduler]] deployment. As such, it uses the jars in the &amp;lt;code&amp;gt;standalone/&amp;lt;/code&amp;gt; directory for building the classpath. If you require a custom classpath on a per job basis, modification of the forking script(s) will be required.&lt;br /&gt;
&lt;br /&gt;
=== Database Configuration Parameters ===&lt;br /&gt;
If your environment does not have database configuration entries in the properties/yaml file accessible to the forked job (e.g. config is done via System properties and/or programmatically), you will want to enable the following option for forked jobs. When enabled, database configuration will be passed into the scripts instead of relying on locating a properties file with the same information.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;com.carfey.obsidian.jvmJobForkingIncludeDbSysParms=true&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Script Customization =&lt;br /&gt;
&lt;br /&gt;
If you aren&#039;t using a [[Getting_Started#Standalone_Scheduler|standalone scheduler]], or you simply need to customize Job Forking, review the following section for detailed documentation on the scripts used and how they can be customized.&lt;br /&gt;
&lt;br /&gt;
== Script Arguments ==&lt;br /&gt;
Obsidian calls the script with the following arguments. &lt;br /&gt;
* &amp;lt;code&amp;gt;stack_file&amp;lt;/code&amp;gt; Always the first argument. Used by the Scheduler Node to be aware of execution exceptions,&lt;br /&gt;
* &amp;lt;code&amp;gt;-argumentsFile&amp;lt;/code&amp;gt; Required if you need database parameters or additional execution data such as job nickname, job class or running host.&lt;br /&gt;
* &amp;lt;code&amp;gt;-classpathScan&amp;lt;/code&amp;gt; Flag indicating the run is a classpath scan request.&lt;br /&gt;
* &amp;lt;code&amp;gt;-jobHistoryId&amp;lt;/code&amp;gt; Required. Job execution instance identifier.&lt;br /&gt;
* &amp;lt;code&amp;gt;-classpathOverride&amp;lt;/code&amp;gt; Optionally provided if the &amp;lt;code&amp;gt;com.carfey.obsidian.forkedJobscriptClasspathOverride&amp;lt;/code&amp;gt; property is present.&lt;br /&gt;
&lt;br /&gt;
The following arguments can be found in the arguments file that is created for each invocation. Job parameters are not included in classpath scan requests. Database parameters are only included if the &amp;lt;code&amp;gt;com.carfey.obsidian.jvmJobForkingIncludeDbSysParms&amp;lt;/code&amp;gt; property is true. All these parameters are parsed into the provided scripts as arguments. See the notes inside each script for more details.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;jobNickname&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;jobClass&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;runningHost&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;dbUrl&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;dbJndi&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;dbUser&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;dbPass&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;dbMaxConn&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;dbConnTimeout&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;dbUnusedConnTimeout&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;dbTablePrefix&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;dbSchema&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Forked Obsidian Job Runner == &lt;br /&gt;
Should you wish to write your own &amp;lt;code&amp;gt;obsidianForkedJob.sh&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;obsidianForkedJob.bat&amp;lt;/code&amp;gt; or wish to modify the one(s) provided, you&#039;ll need to know how to invoke the forked job runtime. The runtime is accessed by invoking the Obsidian class &amp;lt;code&amp;gt;com.carfey.ops.job.ForkedJob&amp;lt;/code&amp;gt;. Its main method &#039;&#039;&#039;must&#039;&#039;&#039; be called with 2 arguments.&lt;br /&gt;
# &amp;lt;code&amp;gt;job_history_id&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;quot;scanClasspath&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
# &amp;lt;code&amp;gt;stack_file&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A number of the other arguments are available to be passed as JVM system properties for database connectivity as noted below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-Dcom.carfey.obsidian.db.url=$dbUrl -Dcom.carfey.obsidian.db.jndiType=$dbJndi -Dcom.carfey.obsidian.db.userId=$dbUser \&lt;br /&gt;
-Dcom.carfey.obsidian.db.password=$dbPass -Dcom.carfey.obsidian.db.maxConnections=$dbMaxConn -Dcom.carfey.obsidian.db.connectionTimeout=$dbConnTimeout \&lt;br /&gt;
-Dcom.carfey.obsidian.db.unusedPoolConnectionTimeoutSeconds=$dbUnusedConnTimeout -Dcom.carfey.obsidian.db.tablePrefix=$dbTablePrefix -Dcom.carfey.obsidian.db.schema=$dbSchema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Per Job Customizations - Classpaths and More ==&lt;br /&gt;
Three other parameters are provided as context for additional customization efforts, such as having alternate classpaths for particular jobs, classpath library ordering, controlling which Java executable is used and so on. Please contact the [[Contact_the_Obsidian_Scheduler_Team |Obsidian support]] team to discuss your needs or with any questions you have.&lt;br /&gt;
&lt;br /&gt;
The scripts parse &amp;lt;code&amp;gt;jobNickname&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;jobClass&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;runningHost&amp;lt;/code&amp;gt; from the arguments file. These can be utilized within the script to determine what specialization to apply. These could be used in conjunction with the default classpath, the &amp;lt;code&amp;gt;classpath_override&amp;lt;/code&amp;gt; or on its own.&lt;br /&gt;
&lt;br /&gt;
For example, if you wanted to use a custom classpath if the &amp;lt;code&amp;gt;job_class&amp;lt;/code&amp;gt; is of type &amp;quot;com.example.OldLibrariesJob&amp;quot; and another one when the job nickname is &amp;quot;New Open Document Format Job&amp;quot;, you might modify the shell script like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if [ &amp;quot;New Open Document Format Job&amp;quot; == &amp;quot;$jobNickname&amp;quot;] ; then&lt;br /&gt;
	cp=/Obsidian-3.0.0/newlibs/odf-4.0-beta.jar:/Obsidian-3.0.0/libs/... rest of classpath here&lt;br /&gt;
elif [ &amp;quot;com.example.OldLibrariesJob&amp;quot; == &amp;quot;$jobClass&amp;quot;] ; then&lt;br /&gt;
	cp=/Obsidian-3.0.0/oldlibs/my-old-lib-1.0a.jar:/Obsidian-3.0.0/libs/... rest of classpath here&lt;br /&gt;
elif [ &amp;quot;&amp;quot; != &amp;quot;$classpathOverride&amp;quot; ]  ; then&lt;br /&gt;
	cp=$classpathOverride&lt;br /&gt;
elif [ &amp;quot;$(expr substr $(uname -s) 1 6)&amp;quot; == &amp;quot;CYGWIN&amp;quot; ] ; then&lt;br /&gt;
	cp=$(find standalone -name &amp;quot;*.jar&amp;quot; -exec printf &amp;quot;{};&amp;quot; &#039;;&#039;)&lt;br /&gt;
else&lt;br /&gt;
	cp=$(find standalone -name &amp;quot;*.jar&amp;quot; -exec printf :{} &#039;;&#039;)&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notes about Spring and JNDI ==&lt;br /&gt;
Since Job Forking is starting a specialized short-lived Obsidian instance designed to either run a classpath scan or execute a job via a Java class&#039; main method, database/email configuration via JNDI and Spring container support are not available out-of-the-box as these are made possible through your deployment and configuration options that Obsidian simply expects to be available. It&#039;s still possible to make use of these, it will just require an intermediary step between the forking scripts and the &amp;lt;code&amp;gt;com.carfey.ops.job.ForkedJob&amp;lt;/code&amp;gt; class.&lt;br /&gt;
&lt;br /&gt;
=== How to Support Spring and JNDI ===&lt;br /&gt;
You need some points of customization to make container resources available.&lt;br /&gt;
&lt;br /&gt;
Possible customizations needed may include:&lt;br /&gt;
* A new entry point class that initializes a Spring Container, calls the Obsidian &amp;lt;code&amp;gt;com.carfey.ops.job.ForkedJob&amp;lt;/code&amp;gt; class and then shuts down the container.&lt;br /&gt;
* A new or existing entry point mechanism that is able to call the Obsidian &amp;lt;code&amp;gt;com.carfey.ops.job.ForkedJob&amp;lt;/code&amp;gt; class in your server container.&lt;br /&gt;
* Forking script modifications to utilize the above.&lt;br /&gt;
&lt;br /&gt;
For example, let&#039;s say you have integrated Spring and Obsidian and want Spring support while using Job Forking.&lt;br /&gt;
&lt;br /&gt;
# Write a new class that initializes your Spring container following the same Obsidian integration as you have done for your primary application. This class must take in the [[#Script_Arguments|Script Arguments]].&lt;br /&gt;
# In code, after initialization is complete, call &amp;lt;code&amp;gt;com.carfey.ops.job.ForkedJob&amp;lt;/code&amp;gt; with the script arguments passed in.&lt;br /&gt;
# When the main method returns, shutdown the Spring container in code.&lt;br /&gt;
# Modify the forking script(s) to call the new class.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Job_Features&amp;diff=4151</id>
		<title>Job Features</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Job_Features&amp;diff=4151"/>
		<updated>2026-07-31T14:49:54Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In Obsidian, everything starts with [[Admin_Jobs|jobs]]. They can be scheduled, constrained by parameters, and even restricted to running on specific hosts.&lt;br /&gt;
&lt;br /&gt;
In addition to job [[Implementing_Jobs|implementation features]], Obsidian supports the following features that apply to the execution and scheduling environment.&lt;br /&gt;
&lt;br /&gt;
= Full Execution History =&lt;br /&gt;
&lt;br /&gt;
Obsidian offers full transparency into when jobs run, complete and if they fail. In addition, Obsidian stores [[Admin_Job_Activity|history]] of any time jobs are skipped, overlapped, missed due to downtime, etc.&lt;br /&gt;
&lt;br /&gt;
Obsidian also stores saved job results, chaining evaluations and stack traces in the event of a job failure.&lt;br /&gt;
&lt;br /&gt;
== Execution Statuses ==&lt;br /&gt;
&lt;br /&gt;
The following are the valid states a job execution may appear in.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Ready&#039;&#039;&#039; - Ready to be executed at its scheduled time and awaiting pickup for execution.&lt;br /&gt;
* &#039;&#039;&#039;Running&#039;&#039;&#039; - Currently executing.&lt;br /&gt;
* &#039;&#039;&#039;Completed&#039;&#039;&#039; - Completed execution without error (i.e. without throwing an uncaught exception). This is a terminal state.&lt;br /&gt;
* &#039;&#039;&#039;Failed&#039;&#039;&#039; - Did not complete successfully. Exception stack trace is saved for review. This is a terminal state.&lt;br /&gt;
* &#039;&#039;&#039;Missed&#039;&#039;&#039; - Could not be executed at its scheduled time due to an outage. This is typically when Obsidian was not running but could also be caused by technical issues such as database outage, inability to verify licenses, etc. This is a terminal state.&lt;br /&gt;
* &#039;&#039;&#039;Conflicted&#039;&#039;&#039; - Currently in an executable window (past its scheduled time but before its pickup buffer has expired), but is conflicted with another job. Equivalent to Ready, but when a conflict exists.&lt;br /&gt;
* &#039;&#039;&#039;Conflict Missed&#039;&#039;&#039; - Was prevented from executing by a conflicting job until after its pickup buffer expired. This is a terminal state.&lt;br /&gt;
* &#039;&#039;&#039;Died&#039;&#039;&#039; - The job execution thread (or forked process) was terminated. This can happen when Obsidian is shut down before a job completes. This is a terminal state.&lt;br /&gt;
* &#039;&#039;&#039;Abandoned&#039;&#039;&#039; - Was in Ready state but its pickup buffer expired without an active scheduler instance available to begin execution. This can also happen when there are active scheduler instances, but due to use of [[Admin_Jobs#Advanced_Options|Fixed Hosts]], no running instances match the host designator(s) assigned to the job. This is a terminal state.&lt;br /&gt;
* &#039;&#039;&#039;Overlapped&#039;&#039;&#039; - Was due to be run at its scheduled time, but was overlapped by a prior executing instance of the job. This means the previous running job&#039;s completion time was after the job&#039;s pickup buffer expired. This is a terminal state.&lt;br /&gt;
* &#039;&#039;&#039;Chain Skipped&#039;&#039;&#039; - Configured as a chain target, but the chaining restrictions were not met, so the job is not executed. This occurs when conditional chain criteria are not met, or the job state does not match one of the trigger states. This is a terminal state.&lt;br /&gt;
* &#039;&#039;&#039;Pending&#039;&#039;&#039; - The state given to an [[Implementing_Jobs#Async_Jobs | Async Job]] that has completed in Obsidian but has not yet been updated via the API to indicate its result.&lt;br /&gt;
&lt;br /&gt;
== Execution Status Flow ==&lt;br /&gt;
&lt;br /&gt;
[[File:obsidian_execution_statuses.png]]&lt;br /&gt;
&lt;br /&gt;
= Date Bound Scheduling =&lt;br /&gt;
&lt;br /&gt;
Obsidian supports setting the job&#039;s runtime schedule and state by date/time windows. This allows you to configure future changes to a schedule or job state. Obsidian also allows these changes to be scheduled in windows, so that a configuration will revert back to previous state at the completion of the window.  This useful feature allows you to prepare for known outages, schedule holiday schedules in advance, and more, all without having to remember to make changes exactly when they need to go live.&lt;br /&gt;
&lt;br /&gt;
Scheduling is done using standard [[Cron]] patterns and custom [[Cron#Recurrence | Recurrence]] extensions.&lt;br /&gt;
&lt;br /&gt;
[[#Custom Calendars|Custom Calendars]] can also be configured with schedule changes to prevent execution on certain dates.&lt;br /&gt;
&lt;br /&gt;
= Parameterization =&lt;br /&gt;
Jobs can also be parameterized, so that jobs can be reused in multiple configurations. Parameters allow for many varying instances of the same job class that differ by the specified parameters. See [[Implementing_Jobs#Parameterization|Parameterization]].&lt;br /&gt;
&lt;br /&gt;
=Host Restrictions =&lt;br /&gt;
Jobs can be restricted to run only on specified hosts. This allows you to use Obsidian in a multi-host environment, even when certain jobs can only run on one of the hosts in the cluster.&lt;br /&gt;
&lt;br /&gt;
A configuration item allows you to specify whether you wish host restrictions to apply on AdHoc jobs or not. It defaults to TRUE on new installations. It can by found under &#039;&#039;&#039;[[Admin Scheduler Settings|Scheduler Settings]] / Job / adHocJobsRespectFixedHostsRestrictions&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=Host Preference =&lt;br /&gt;
&lt;br /&gt;
In addition to strict host selections, Obsidian supports the concept of host preference. For each job, you can select this option to enable prioritizing specific hosts for job execution. When a job configured with this setting is ready for execution, Obsidian will select the most preferred host to execute the job on, and if no preferred host is available, execution will fall back to any available host. This is configurable on the [[Admin_Jobs#Adding_.26_Editing_Jobs|job configuration screen]].&lt;br /&gt;
&lt;br /&gt;
=Recovery=&lt;br /&gt;
&lt;br /&gt;
Obsidian has extensive recovery support to ensure reliable execution in the event of outages.&lt;br /&gt;
&lt;br /&gt;
Each job is configured with a &#039;&#039;Pickup Buffer&#039;&#039; which indicates how many minutes after the scheduled time a job is considered valid for regular pickup and execution.&lt;br /&gt;
&lt;br /&gt;
Every configured job also specifies a &#039;&#039;Recovery Type&#039;&#039;, which indicates how to handle a job that is not run within the &#039;&#039;Pickup Buffer&#039;&#039;. This might happen because of a configured [[Admin_Job_Conflicts|conflict]] or because the scheduler was not running. Obsidian gives you fine control over how to handle these situations by exposing the following &#039;&#039;Recovery Types&#039;&#039;:&lt;br /&gt;
* &#039;&#039;Last&#039;&#039; indicates that only the last missed job run will be recovered.&lt;br /&gt;
* &#039;&#039;Conflicted&#039;&#039; indicates that only conflicted job runs will be recovered.&lt;br /&gt;
* &#039;&#039;All&#039;&#039; indicates that all job runs will be recovered.&lt;br /&gt;
* &#039;&#039;None&#039;&#039; indicates that no job runs will be recovered.&lt;br /&gt;
&lt;br /&gt;
Obsidian also supports individual job failure recovery through automated retries.&lt;br /&gt;
Each job can be configured to use &#039;&#039;Auto Retries&#039;&#039;. Specify a &#039;&#039;Count&#039;&#039; that will tell Obsidian how many times to keep trying (essentially an auto-resubmit) a failed job. &lt;br /&gt;
&lt;br /&gt;
You can use &#039;&#039;Interval&#039;&#039; and &#039;&#039;Exponential&#039;&#039; as well. Use &#039;&#039;Interval&#039;&#039; to indicate the minimum number of minutes from the last failure to the next retry.&lt;br /&gt;
Use &#039;&#039;Exponential&#039;&#039; if you want the auto retries interval to exponentially increase as retries are attempted. For example, if you set the interval minutes to 5 and check Exponential, the first retry will be 5 minutes after failure, the second retry after a subsequent failure will be 10 minutes later, then 20 minutes, and so on.&lt;br /&gt;
&lt;br /&gt;
= Resubmission &amp;amp; Ad Hoc Runs =&lt;br /&gt;
Did a job fail unexpectedly? Or do you just need to run a job one time? Obsidian gives you the flexibility to resubmit failed jobs or submit them for a single execution regardless of their execution schedule. This can be done via our [[REST_API|REST API]], [[Embedded_API|Embedded API]] or via the [[Admin_Job_Activity|admin web application]].&lt;br /&gt;
&lt;br /&gt;
In addition, when you submit an ad hoc job run, you can supply job parameters which apply only to that execution. This can be useful for executing jobs for a particular one-time need. For example, if you run out of disk space on a host, you may have a log archival job which can be run against a specific directory for a single execution.&lt;br /&gt;
&lt;br /&gt;
Multiple Ad Hoc Runs may be submitted per minute for both immediate (next minute) execution and future execution.&lt;br /&gt;
&lt;br /&gt;
= Forced Termination (interruptable jobs) =&lt;br /&gt;
Jobs that appear to be hung or need to be interrupted for any other reason can be terminated by having the job implement a simple interface. See [[Implementing_Jobs#Interruptable_Jobs|interruptable jobs]].&lt;br /&gt;
&lt;br /&gt;
=Expected Length Handling (job execution duration) =&lt;br /&gt;
&lt;br /&gt;
Jobs can have an expected runtime length set, which causes a Warning level event to be triggered if a job falls outside of the expected range. This is used primarily for notifications. Users can be alerted in the event of a long-running (or unexpectedly short) job by configuring the appropriate [[Admin Notifications|Notifications]]. &lt;br /&gt;
&lt;br /&gt;
[[Implementing_Jobs#Interruptable_Jobs|Interruptable Jobs]] also support auto interruption when execution length exceeds maximum expected run length.&lt;br /&gt;
&lt;br /&gt;
= Custom Calendars =&lt;br /&gt;
[[Admin_Custom_Calendars|Custom calendars]] allow you to create named sets of dates that are used to prevent job executions. When setting a schedule for a job, you can select a custom calendar so that job execution is suppressed on any date defined in the custom calendar, regardless of the cron pattern used.&lt;br /&gt;
&lt;br /&gt;
Common uses would be to define corporate holidays or those of your partners. Custom calendars are optional applications to the aforementiond [[Job_Features#Date_Bound_Scheduling|date bound scheduling]].&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Installation_Guide&amp;diff=4150</id>
		<title>Installation Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Installation_Guide&amp;diff=4150"/>
		<updated>2026-07-31T14:49:54Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;This installation guide generally applies to Obsidian versions 4.0 and newer but is specific to 7.0. You may see a few minor differences in 4.x, 5.x and 6.x releases.&lt;br /&gt;
&lt;br /&gt;
This installation guide is a companion to the Obsidian Installer UI. Its purpose is to provide additional detail as to the meaning of various inputs and to provide guidance on advanced usage of the installer.&lt;br /&gt;
&lt;br /&gt;
The installer itself installs and configures the artifacts. The configured artifacts will have their [[Advanced_Configuration#Properties_File|Properties]] file configured according to the choices made during the installation process. You may always change these later or use one of the [[Advanced_Configuration#Properties_File|override]] mechanisms.&lt;br /&gt;
== Running the Obsidian Installer ==&lt;br /&gt;
The installer is an executable JAR file in the Obsidian download zip package available on our [https://web.obsidianscheduler.com/download/ download page].&lt;br /&gt;
&lt;br /&gt;
The installer can be run from the command line as a graphical user interface using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar&amp;lt;/code&amp;gt; or in interactive console mode using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar -console&amp;lt;/code&amp;gt;. Note that you will have to replace the JAR file name with the actual versioned name in your installation.&lt;br /&gt;
&lt;br /&gt;
On some platforms, simple double-clicking the JAR file will start it in graphical interface mode.&lt;br /&gt;
&lt;br /&gt;
If you are doing a version upgrade of Obsidian or are otherwise uninterested in actually configuring the artifacts, you can run the quick start mode to get the default configured artifacts using &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar h2-tomcat-quick-start.xml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Obsidian Installer Artifacts ==&lt;br /&gt;
&lt;br /&gt;
The Obsidian Installer installs and configures a number of artifacts. You can choose which artifacts to create, but most users can leave the default options selected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Installation-Packages.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Obsidian WAR&#039;&#039;&#039; - This is the Obsidian Web Admin UI with scheduler component enabled. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/obsidian.war!/WEB-INF/classes/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Standalone Admin WAR&#039;&#039;&#039; - This is the Obsidian Web Admin UI with scheduler component disabled. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/standaloneObsidianAdmin.war!/WEB-INF/classes/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Standalone Obsidian Runtime&#039;&#039;&#039; - This is a runtime folder containing the libraries and configuration necessary for running the Obsidian Scheduler component either as a standalone module or for use as an Embedded Scheduler in your application. Configuration will be found at &#039;&#039;INSTALL_PATH&#039;&#039;/standalone/obsidian-props.jar!/com.carfey.properties.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian License Proxy&#039;&#039;&#039; - Allows for local license leasing from a [[Key_Server_Proxy|Key Server Proxy]] that ultimately leases licenses from the Obsidian License server.&lt;br /&gt;
# &#039;&#039;&#039;Documentation&#039;&#039;&#039; - README and Embedded API Javadoc.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; — An embedded Apache Tomcat runtime for running Obsidian locally without an external servlet container. Requires the Obsidian WAR pack.&lt;br /&gt;
# &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; — An embedded Apache Tomcat runtime for running the standalone admin WAR locally. Requires the Obsidian Standalone Admin WAR pack.&lt;br /&gt;
# &#039;&#039;&#039;License&#039;&#039;&#039; - Obsidian and 3rd party license information.&lt;br /&gt;
&lt;br /&gt;
==== Deployment options ====&lt;br /&gt;
&lt;br /&gt;
Obsidian 7.0.0 offers four deployment profiles in the installer:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Obsidian WAR&#039;&#039;&#039; — deploy &#039;&#039;&#039;obsidian.war&#039;&#039;&#039; to your own servlet container (Tomcat, WebLogic, etc.).&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; — adds an embedded Tomcat runtime and &#039;&#039;&#039;webObsidian&#039;&#039;&#039; scripts to run &#039;&#039;&#039;obsidian.war&#039;&#039;&#039; locally (&#039;&#039;&#039;start scheduler&#039;&#039;&#039;). &#039;&#039;&#039;Requires&#039;&#039;&#039; the Obsidian WAR pack.&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Standalone Admin WAR&#039;&#039;&#039; — admin console WAR for an external servlet container.&lt;br /&gt;
* &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; — embedded runtime for the admin WAR (&#039;&#039;&#039;start adminOnly&#039;&#039;&#039;). &#039;&#039;&#039;Requires&#039;&#039;&#039; the Standalone Admin WAR pack.&lt;br /&gt;
&lt;br /&gt;
Embedded Tomcat does &#039;&#039;&#039;not&#039;&#039;&#039; replace the WAR on disk — it runs the installed WAR via the helper. Do not select an embed pack without its matching WAR pack. For how to start, stop and change the ports of the embedded runtime, see [[Getting_Started#Starting_Obsidian_with_embedded_Tomcat|Starting Obsidian with embedded Tomcat]].&lt;br /&gt;
&lt;br /&gt;
==== Linux desktop and systemd integration ====&lt;br /&gt;
&lt;br /&gt;
On a Unix host, an embedded Tomcat pack also copies a &#039;&#039;&#039;linux/&#039;&#039;&#039; directory under your install path holding user-level systemd and desktop-entry templates, and the installer offers to activate them. Nothing is installed system-wide and no root privileges are required.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Embedded Tomcat - Linux integration (optional)&#039;&#039;&#039; panel carries the checkbox &#039;&#039;&#039;Install Linux desktop and user systemd integration&#039;&#039;&#039; (unchecked by default). Tick it to have the installer run the integration script for you; the option only takes effect if you also selected an embedded Tomcat pack. Leaving it unchecked still copies the templates, so you can opt in later by running this from the install directory:&lt;br /&gt;
&lt;br /&gt;
 ./linux/install-embedded-linux-integration.sh &amp;quot;$PWD&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Either route writes two files, for the installing user only:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;~/.config/systemd/user/obsidian-embedded@scheduler.service&#039;&#039;&lt;br /&gt;
* &#039;&#039;~/.local/share/applications/obsidian-embedded.desktop&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;XDG_CONFIG_HOME&#039;&#039; and &#039;&#039;XDG_DATA_HOME&#039;&#039; are honoured when set.)&lt;br /&gt;
&lt;br /&gt;
For an automated (headless) install, request the integration with:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;entry key=&amp;quot;embed.linux.integration&amp;quot; value=&amp;quot;true&amp;quot;/&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This panel is &#039;&#039;&#039;not shown on Windows installs&#039;&#039;&#039; — the systemd and desktop templates are Linux-only. On Windows, run &#039;&#039;&#039;webObsidian.bat&#039;&#039;&#039; manually or wrap it with your own service tooling.&lt;br /&gt;
&lt;br /&gt;
Installing these files does not enable or start anything. To enable, stop, reconfigure or remove the service, see [[Getting_Started#Running_Obsidian_as_a_Linux_user_service|Running Obsidian as a Linux user service]].&lt;br /&gt;
&lt;br /&gt;
== Obsidian Configuration ==&lt;br /&gt;
&lt;br /&gt;
The installer will guide you through the configuration Obsidian.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Obsidian.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
First, you must choose what [[Authenticator]] mechanism will be used within Obsidian. &#039;&#039;Native (Database)&#039;&#039; authentication requires no additional configuration and is what most users will select. Select &#039;&#039;Other&#039;&#039; if you have implemented your own authentication mechanism, OAuth (OIDC), SAML or LDAP according to your desired mechanism. &lt;br /&gt;
&lt;br /&gt;
Next, you&#039;ll want to select the email usage type. We highly recommend you configure Obsidian for email use as it will allow you to benefit from the event [[Event_Notifications|notification]] and [[Admin_Notifications|subscription]] support in Obsidian.&lt;br /&gt;
&lt;br /&gt;
Then you&#039;ll configure the log file location, license key (optional) and registered company name (optional). Registered company name is only required for our Site License users.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== OAuth / OIDC Configuration ====&lt;br /&gt;
&lt;br /&gt;
If you selected &#039;&#039;&#039;OAuth (OIDC)&#039;&#039;&#039; as the authentication type, the installer displays additional panels for OAuth configuration.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Provider and Common Settings =====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-OAuth_OIDC.png]]&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Identity Provider&#039;&#039;&#039; — Select your provider: Keycloak, Microsoft Entra ID, Okta, Auth0, or Generic OIDC.&lt;br /&gt;
* &#039;&#039;&#039;SSO Button label&#039;&#039;&#039; — Label shown on the &amp;quot;Sign in with…&amp;quot; button (e.g. &#039;&#039;Keycloak&#039;&#039;, &#039;&#039;Acme Corp SSO&#039;&#039;). If left blank the button reads &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
* &#039;&#039;&#039;Redirect URI&#039;&#039;&#039; — The callback URL Obsidian receives after IdP login. Must be registered at the IdP (e.g. &#039;&#039;https://app.example.com/oauth/callback&#039;&#039;).&lt;br /&gt;
* &#039;&#039;&#039;Logout mode&#039;&#039;&#039; — &#039;&#039;local&#039;&#039; (default) or &#039;&#039;rp&#039;&#039; (RP-initiated logout via the IdP&#039;s end_session_endpoint).&lt;br /&gt;
&lt;br /&gt;
===== Provider-Specific Settings =====&lt;br /&gt;
&lt;br /&gt;
Fill in the connection details for your chosen provider.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Keycloak.png]]&lt;br /&gt;
&lt;br /&gt;
;Keycloak&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — e.g. &#039;&#039;https://keycloak.example.com/realms/myrealm&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from the Keycloak client settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — recommended: &#039;&#039;openid profile email groups&#039;&#039;. &lt;br /&gt;
Note: Keycloak does not include a &#039;&#039;groups&#039;&#039; claim by default — a Group Membership mapper must be configured in the client&#039;s Mappers tab.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Microsoft-Entra-ID.png]]&lt;br /&gt;
&lt;br /&gt;
;Microsoft Entra ID&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — &#039;&#039;https://login.microsoftonline.com/&amp;lt;tenant-id&amp;gt;/v2.0&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; — the Application (client) ID from the Azure portal.&lt;br /&gt;
* &#039;&#039;&#039;Client Secret&#039;&#039;&#039; — a client secret value from the Azure portal.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email&#039;&#039;. &lt;br /&gt;
Note: the &#039;&#039;groups&#039;&#039; claim contains GUIDs by default; configure Optional Claims in the Azure portal to receive group display names. &lt;br /&gt;
&#039;&#039;&#039;Multi-tenant applications are not supported in Obsidian 7.0.0.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Okta.png]]&lt;br /&gt;
&lt;br /&gt;
;Okta&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — use the &#039;&#039;&#039;Custom Authorization Server&#039;&#039;&#039; URL, e.g. &#039;&#039;https://&amp;lt;domain&amp;gt;.okta.com/oauth2/default&#039;&#039;. Do &#039;&#039;&#039;not&#039;&#039;&#039; use the Org Authorization Server URL.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from Okta application settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email groups&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Auth0.png]]&lt;br /&gt;
&lt;br /&gt;
;Auth0&lt;br /&gt;
* &#039;&#039;&#039;Domain / Issuer URL&#039;&#039;&#039; — e.g. &#039;&#039;https://&amp;lt;domain&amp;gt;.auth0.com/&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from Auth0 application settings.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — &#039;&#039;openid profile email&#039;&#039;.&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; Auth0 issues opaque access tokens by default which are &#039;&#039;&#039;not supported&#039;&#039;&#039; on Obsidian&#039;s REST/Bearer path — configure a custom API in Auth0 to get JWT access tokens.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-Configuring-Generic_OIDC.png]]&lt;br /&gt;
&lt;br /&gt;
;Generic OIDC&lt;br /&gt;
* &#039;&#039;&#039;Issuer URL&#039;&#039;&#039; — the base URL; Obsidian appends &#039;&#039;/.well-known/openid-configuration&#039;&#039; to discover endpoints.&lt;br /&gt;
* &#039;&#039;&#039;Client ID&#039;&#039;&#039; and &#039;&#039;&#039;Client Secret&#039;&#039;&#039; from the provider.&lt;br /&gt;
* &#039;&#039;&#039;Scopes&#039;&#039;&#039; — at minimum &#039;&#039;openid&#039;&#039;; add &#039;&#039;profile&#039;&#039; and your groups scope as needed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== Group → Role Mapping =====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-7.0.0-OAuth-Group-Role-Mapping.png]]&lt;br /&gt;
&lt;br /&gt;
The final OAuth panel maps IdP group values to Obsidian roles. &#039;&#039;&#039;Require role assignment&#039;&#039;&#039; (default yes) — when enabled, users with no matching roles cannot complete browser login. At least one mapping to the &#039;&#039;&#039;Admin&#039;&#039;&#039; role is required. The installer supports up to 10 group→role pairs; additional pairs can be added directly to the configuration file after install. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties]] for the full property reference.&lt;br /&gt;
&lt;br /&gt;
===== Registering the redirect URI at the IdP =====&lt;br /&gt;
&lt;br /&gt;
Before starting Obsidian, register the redirect URI shown in the installer at your IdP: Keycloak — Client → Settings → Valid Redirect URIs; Entra ID — App Registration → Authentication → Redirect URIs; Okta — Application → General Settings → Sign-in Redirect URIs; Auth0 — Application → Settings → Allowed Callback URLs. The URI must match &#039;&#039;com.carfey.suite.security.OAuthAuthenticator.redirectUri&#039;&#039; exactly, including scheme and port.&lt;br /&gt;
&lt;br /&gt;
==== LDAP Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.LDAP.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve selected LDAP Authentication, this screen takes you through configuring the server address and the various elements used to grant access. You should familiarize yourself with Obsidian [[Authenticator#Roles|Roles]]. The &#039;&#039;Access DN&#039;&#039; configuration element grants Read access to Obsidian application. You may use the same DN for more than one Role should you so wish.&lt;br /&gt;
&lt;br /&gt;
==== Custom Authenticator Configuration====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.CustomAuthenticator.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have chosen to use your own authentication mechanism, you must enter the fully qualified classname here.&lt;br /&gt;
&lt;br /&gt;
==== Database Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Database.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Next comes database configuration. If you are using JNDI, leave the username and password fields blank. If using JDBC URL, username and password fields are required.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;Database connections per instance&#039;&#039; and &#039;&#039;Database connection timeout(millis)&#039;&#039; fields are required and provide directive to the connection pool.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Database table name prefix&#039;&#039; is optional and is typically used when Obsidian will be colocated in an existing database/schema.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Database schema (Oracle/PostgreSQL)&#039;&#039; - This is used to allow for an alternate schema other than the default user&#039;s schema. We also recommend setting this value with Oracle/PostgreSQL when using JNDI as it allows more efficient database metadata loading.&lt;br /&gt;
&lt;br /&gt;
By default, the Obsidian installation will only include the JDBC libraries necessary for your particular database. At times you may wish to include the others to be able to change between databases. In those cases, check &#039;&#039;Include all supported JDBC libs&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Email Configuration ====&lt;br /&gt;
&lt;br /&gt;
If not using JNDI for mail sessions, provide the server and authentication details as required.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.SSL.Email.png]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.TLS.Email.png]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.Open.Email.png]]&lt;br /&gt;
&lt;br /&gt;
==== JNDI Configuration ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Configure.JNDI.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you have selected JNDI for mail sessions, provide the JNDI path here. If you are using JNDI for Database connections, specify the database type.&lt;br /&gt;
&lt;br /&gt;
==== Configuring 3rd Party Library Conflict Management ====&lt;br /&gt;
[[Image:JarJar-Obsidian-6.0.0.png]]&lt;br /&gt;
&lt;br /&gt;
The Obsidian installer allows you to use [https://code.google.com/archive/p/jarjar JarJar] to handle potential conflicts between Obsidian&#039;s use of 3rd party libraries and versions used within your application. On this screen, simply select which libraries to which you wish to apply the JarJar bytecode modification process. This is optional and is skipped if no libraries are selected. Any jars not listed for which you are using more recent versions can be used and are therefore not included as options in the JarJar processing.&lt;br /&gt;
&lt;br /&gt;
==== Selecting Script Libraries ====&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-6.0.0-Choose-Scripting-Libraries.png]]&lt;br /&gt;
&lt;br /&gt;
The Obsidian installer allows you to select which script libraries you wish to include. Jython and JRuby are incompatible with each other, so the installer will only allow one of the two selected. If all script libraries are deselected, the only script jobs that can be run in Obsidian will be Javascript jobs as that engine is built into Java.&lt;br /&gt;
&lt;br /&gt;
==== Choosing Email Support ====&lt;br /&gt;
As of Obsidian 5.2.0, you can choose between JavaMail (javax) and Jakarta mail (Jakarta EE) implementations. When choosing Jakarta, you can also choose to bundle the Angus Jakarta compatible implementation. &lt;br /&gt;
&lt;br /&gt;
If you are using JNDI, this will only include the relevant support in the Obsidian WAR artifacts and the actual libraries in use must be provided by you and bundled with your container. If the standalone scheduler is selected along with JNDI, JNDI is assumed only relevant for WARs. As such, default libraries for either JavaMail or Jakarta will be included in the standalone installation.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.EmailImplementation.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
==== Additional configuration items ====&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 5.5.0, if you require any additional configuration items such as additional appenders/loggers or event hook configurations, you can add them here.&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.5.0.Extra.Configuration.png]]&lt;br /&gt;
&lt;br /&gt;
==== Offline or restricted-network install ====&lt;br /&gt;
&lt;br /&gt;
Obsidian 7.0.0 resolves third-party libraries when you run the installer. For environments without direct access to Maven Central:&lt;br /&gt;
&lt;br /&gt;
# Pre-stage a local dependency cache containing every coordinate listed in the Obsidian dependency inventory for your release (contact Carfey support or your account team for the checklist file matching &#039;&#039;&#039;Obsidian-Install-7.0.0.jar&#039;&#039;&#039;).&lt;br /&gt;
# Copy the cache to the install host. Two layouts are supported:&lt;br /&gt;
#* &#039;&#039;&#039;Flat:&#039;&#039;&#039; one file per installed JAR name directly under the cache root.&lt;br /&gt;
#* &#039;&#039;&#039;File Maven repo:&#039;&#039;&#039; &#039;&#039;&#039;&amp;lt;group&amp;gt;/&amp;lt;artifact&amp;gt;/&amp;lt;version&amp;gt;/&amp;lt;installed-filename&amp;gt;.jar&#039;&#039;&#039;&lt;br /&gt;
# Run the installer with offline mode and the cache directory:&lt;br /&gt;
&lt;br /&gt;
 java -Dobsidian.install.deps.offline=true -Dobsidian.install.deps.cache.dir=/path/to/cache -jar Obsidian-Install-7.0.0.jar&lt;br /&gt;
&lt;br /&gt;
If a required library is missing from the cache, the install stops with an error naming the coordinate — add that artifact to the cache and re-run.&lt;br /&gt;
&lt;br /&gt;
===== Corporate Maven mirror (online) =====&lt;br /&gt;
&lt;br /&gt;
When your site mirrors Maven Central, point the installer at your repository base URL:&lt;br /&gt;
&lt;br /&gt;
 java -Dobsidian.install.deps.repo.url=https://nexus.example.com/repository/maven-public/ -jar Obsidian-Install-7.0.0.jar&lt;br /&gt;
&lt;br /&gt;
The installer reuses resolved artifacts for the duration of one install session.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Completing the Installation ==&lt;br /&gt;
&lt;br /&gt;
[[Image:Obsidian-5.0.0.Installer.Finished.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Once you&#039;ve completed the Installation and Configuration screens, fully configured Obsidian Scheduler artifacts are now ready for you to use in the installation path you selected.&lt;br /&gt;
&lt;br /&gt;
Should you wish to automate future installations with the same configuration, click &#039;&#039;Generate an automatic installation script&#039;&#039;. This will prompt you to save an XML file that can be used for future installations using the automated install procedure &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar my-obsidian-configuration.xml&amp;lt;/code&amp;gt;. Every effort is made to ensure compatibility of these automated install files between versions. Any incompatibility will be noted in the [[Release_Notes|Release Notes]]. These automated installer files can also be used as templates for other environments, modifying them as necessary.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Initializing_and_Restoring&amp;diff=4149</id>
		<title>Initializing and Restoring</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Initializing_and_Restoring&amp;diff=4149"/>
		<updated>2026-07-31T14:49:53Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;At times, you may wish to perform initialization, backup or restoration of an Obsidian installation. For example, you may wish to take a known good configuration from a development environment and apply it to a test environment. &lt;br /&gt;
&lt;br /&gt;
As of Obsidian 6.3.0, we introduce support for [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/SchedulableJob.Initializable.html annotation based initialization] that leverages our [[Implementing_Jobs#Classpath_Scanning|classpath scanning]].&lt;br /&gt;
&lt;br /&gt;
In Obsidian 3.0.0, we introduce support that leverages the [[Embedded_API#SystemRestoreManager_API|System Restore]] functionality of our API. Both the [[Embedded_API|Embedded API]] and [[REST_API|REST API]] expose functions for retrieving and updating the full configuration for an Obsidian installation. Rather than require you to individually retrieve all the configuration items and then apply them elsewhere, a full JSON snapshot can be generated and imported, allowing for easy backups and restores.&lt;br /&gt;
&lt;br /&gt;
== Uses ==&lt;br /&gt;
&lt;br /&gt;
=== Annotation Initialization ===&lt;br /&gt;
&#039;&#039;As of Obsidian 6.3.0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Job configuration can be automated into target environments via via annotations and [[Implementing_Jobs#Classpath_Scanning|classpath scanning]]. The base annotation to configure a job is [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/SchedulableJob.Initializable.html Initializable]. It is a [https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/annotation/Repeatable.html @Repeatable] annotation. On every startup, the nickname in the annotation is used to determine if that job &amp;lt;code&amp;gt;Initializable&amp;lt;/code&amp;gt; reference has been initialized before, either via annotation, API or UI. Any nickname conflict will prevent initialization. Initialization is best effort per annotation, with errors logged and event notifications dispatched.&lt;br /&gt;
&lt;br /&gt;
Chains can be initialized via [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/SchedulableJob.InitializableChainAnnotation.html InitializableChainAnnotation] using nicknames. It is also a [https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/annotation/Repeatable.html @Repeatable] annotation. Descriptions along with source and target job are used to determine if the chain has been initialized previously, since source/target pairs can be repeated with different setups. Conditional chaining can be done via [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/SchedulableJob.InitializableChainConditionAnnotation.html InitializableChainConditionAnnotation]. Again, it is a [https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/annotation/Repeatable.html @Repeatable] annotation. &lt;br /&gt;
&lt;br /&gt;
=== Backup or Export ===&lt;br /&gt;
Use the System Restore [[REST_Endpoints#GET_a_system_restore_configuration|GET REST endpoint]] or the [[Embedded_API#Get_System_Restore_Configuration|equivalent Embedded API]] to retrieve a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/system/restore/SystemRestoreConfiguration.html SystemRestoreConfiguration]. Store the JSON representation in your backup media.&lt;br /&gt;
&lt;br /&gt;
=== Transfer or Restore a Configuration ===&lt;br /&gt;
Follow the [[Initializing_and_Restoring#Backup_or_Export|Backup]] instructions. Then, using the backup JSON, you can either place it on the file-system/classpath as described in [[#Startup_Initialization|Startup Initialization]], or use it in conjunction with the System Restore [[REST_Endpoints#PUT_a_system_restore_configuration|PUT REST endpoint]] or [[Embedded_API#Update_System_Restore_Configuration|equivalent Embedded API]]. &lt;br /&gt;
&lt;br /&gt;
You may choose to update parts of the exported configuration depending on your needs, or to set new user passwords.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Users do not include the current passwords since they cannot be determined, and also as a security measure. All users will be returned with the password &amp;quot;changeme&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Startup Initialization ===&lt;br /&gt;
By default, Obsidian will look for a file on the classpath named &amp;lt;code&amp;gt;/obsidianInitialization.json&amp;lt;/code&amp;gt; containing configuration data in the format described in [[#JSON_Configuration_Format|JSON Configuration Format]]. &lt;br /&gt;
&lt;br /&gt;
You may override the classpath resource name using the system property &amp;lt;code&amp;gt;obsidianInitClasspath&amp;lt;/code&amp;gt;. For example, you could add the java system property &amp;lt;code&amp;gt;-DobsidianInitClasspath=/com/mycompany/obsidianInit.json&amp;lt;/code&amp;gt;. You can also use a file-based resource by using the system property &amp;lt;code&amp;gt;obsidianInitFile&amp;lt;/code&amp;gt;. For example, you could add the java system property &amp;lt;code&amp;gt;-DobsidianInitFile=/var/obsidian-3.0.0/obsidianScheduleInitialization.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Initialization only runs on scheduler instances. This means that a standalone Obsidian web application with no scheduler running will not do any initialization based on the presence of the appropriate JSON file.&lt;br /&gt;
&lt;br /&gt;
== JSON Configuration Format ==&lt;br /&gt;
The file format is a JSON serialized version of the [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/system/restore/SystemRestoreConfiguration.html SystemRestoreConfiguration] class. Here we will define each configuration item and any special conditions associated with each one. A sample JSON is [[#Sample|provided]].&lt;br /&gt;
&lt;br /&gt;
=== Jobs ===&lt;br /&gt;
The &amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt; key has a list of [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/system/restore/JobSaveRequest.html JobSaveRequest] objects. Each of these represents a configured job in Obsidian. This JobSaveRequest is very much like a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobCreationRequest.html JobCreationRequest] but having two extra attributes to handle the case where a job with the same nickname already exists. The &amp;lt;code&amp;gt;updateAttributes&amp;lt;/code&amp;gt; flag is used to determine whether the job attributes (jobClass, recoveryType, pickupBufferMinutes, etc.) will be updated when the job exists. The &amp;lt;code&amp;gt;updateSchedule&amp;lt;/code&amp;gt; flag is used to determine whether to apply the current schedule and any additional &amp;lt;code&amp;gt;jobSchedules&amp;lt;/code&amp;gt; when the job already exists. If any of the schedules&#039; end dates are in the past, they will be skipped.&lt;br /&gt;
&lt;br /&gt;
=== Job Chains ===&lt;br /&gt;
The &amp;lt;code&amp;gt;chains&amp;lt;/code&amp;gt; key is a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/system/restore/ChainsRequest.html ChainsRequest] that houses a collection of chain configurations and a &amp;lt;code&amp;gt;replaceAll&amp;lt;/code&amp;gt; attribute. The replaceAll attribute is used when chains already exist in the system. If chains exist and the flag is not set to true, the existing chains are left as is. If set to true, all existing chains are deleted and the new set of chains are created.&lt;br /&gt;
&lt;br /&gt;
=== Job Conflicts ===&lt;br /&gt;
The &amp;lt;code&amp;gt;conflicts&amp;lt;/code&amp;gt; key is similar to the [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/ConflictUpdateRequest.html ConflictUpdateRequest] but it uses job nicknames instead of job IDs. If an empty list is supplied, all chains will be deleted. Note that this is different from a null or missing value.&lt;br /&gt;
&lt;br /&gt;
=== Users ===&lt;br /&gt;
The &amp;lt;code&amp;gt;users&amp;lt;/code&amp;gt; key has a list of [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/system/restore/UserSaveRequest.html UserSaveRequest] objects. These are similar to the standard [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/user/UserCreationRequest.html UserCreationRequest], but they contain an &amp;lt;code&amp;gt;update&amp;lt;/code&amp;gt; attribute that is used when the user already exists. If the same user name exists and the flag is not set to true, the user is left as is. Otherwise, the user is created or updated as the case dictates.&lt;br /&gt;
&lt;br /&gt;
=== Global Parameters ===&lt;br /&gt;
The &amp;lt;code&amp;gt;globalParameters&amp;lt;/code&amp;gt; key is a list of [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/GlobalParameter.html GlobalParameter] objects that are to be set into the system. Any current global parameters are either deleted or overridden.&lt;br /&gt;
&lt;br /&gt;
=== Custom Calendars ===&lt;br /&gt;
The &amp;lt;code&amp;gt;customCalendars&amp;lt;/code&amp;gt; key is a list of [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/calendar/CustomCalendarUpdateRequest.html CustomCalendarUpdateRequest] objects. Creates or updates as the case dictates by matching on the name.&lt;br /&gt;
&lt;br /&gt;
=== System Parameters ===&lt;br /&gt;
The &amp;lt;code&amp;gt;systemParameters&amp;lt;/code&amp;gt; key is a list of [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/system/SystemParameter.html SystemParameter] objects. Updates all parameters given.&lt;br /&gt;
&lt;br /&gt;
=== Subscribers ===&lt;br /&gt;
The &amp;lt;code&amp;gt;subscribers&amp;lt;/code&amp;gt; key is a list of [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/system/restore/SubscriberSaveRequest.html SubscriberSaveRequest] objects. Creates or updates as the case dictates by matching on the email address.&lt;br /&gt;
&lt;br /&gt;
=== Templates ===&lt;br /&gt;
The &amp;lt;code&amp;gt;templates&amp;lt;/code&amp;gt; key is a list of [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/system/restore/TemplateSaveRequest.html TemplateSaveRequest] objects. Creates or updates as the case dictates by matching on the name.&lt;br /&gt;
&lt;br /&gt;
== Sample ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;jobs&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;updateSchedule&amp;quot;: false,&lt;br /&gt;
         &amp;quot;updateAttributes&amp;quot;: false,&lt;br /&gt;
         &amp;quot;jobSchedules&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
               &amp;quot;state&amp;quot;: &amp;quot;DISABLED&amp;quot;,&lt;br /&gt;
               &amp;quot;effectiveDate&amp;quot;: &amp;quot;2015-03-20T10:35:00-0700&amp;quot;,&lt;br /&gt;
               &amp;quot;endDate&amp;quot;: &amp;quot;2999-12-31T23:59:00-0800&amp;quot;&lt;br /&gt;
            }&lt;br /&gt;
         ],&lt;br /&gt;
         &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.maint.LogCleanupJob&amp;quot;,&lt;br /&gt;
         &amp;quot;minExecutionDuration&amp;quot;: &amp;quot;2s&amp;quot;,&lt;br /&gt;
         &amp;quot;maxExecutionDuration&amp;quot;: &amp;quot;3s&amp;quot;,&lt;br /&gt;
         &amp;quot;nickname&amp;quot;: &amp;quot;Log Cleanup&amp;quot;,&lt;br /&gt;
         &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
         &amp;quot;hosts&amp;quot;: [ &amp;quot;Demo-PC&amp;quot; ],&lt;br /&gt;
         &amp;quot;ordinalParameters&amp;quot;: [&lt;br /&gt;
           &amp;quot;ordinal&amp;quot;: 0,&lt;br /&gt;
           &amp;quot;parameters&amp;quot;: [&lt;br /&gt;
              {&lt;br /&gt;
                 &amp;quot;name&amp;quot;: &amp;quot;level&amp;quot;,&lt;br /&gt;
                 &amp;quot;type&amp;quot;: &amp;quot;STRING&amp;quot;,&lt;br /&gt;
                 &amp;quot;value&amp;quot;: &amp;quot;ALL&amp;quot;&lt;br /&gt;
              },&lt;br /&gt;
              {&lt;br /&gt;
                 &amp;quot;name&amp;quot;: &amp;quot;maxAgeDays&amp;quot;,&lt;br /&gt;
                 &amp;quot;type&amp;quot;: &amp;quot;INTEGER&amp;quot;,&lt;br /&gt;
                 &amp;quot;value&amp;quot;: &amp;quot;120&amp;quot;&lt;br /&gt;
              }&lt;br /&gt;
           ]&lt;br /&gt;
         ],&lt;br /&gt;
         &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
         &amp;quot;hostPreference&amp;quot;: true,&lt;br /&gt;
         &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
         &amp;quot;state&amp;quot;: &amp;quot;ENABLED&amp;quot;,&lt;br /&gt;
         &amp;quot;schedule&amp;quot;: &amp;quot;* * * * *&amp;quot;,&lt;br /&gt;
         &amp;quot;effectiveDate&amp;quot;: &amp;quot;2015-01-01T00:00:00-0700&amp;quot;&lt;br /&gt;
         &amp;quot;endDate&amp;quot;: &amp;quot;2015-03-20T10:34:00-0700&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;updateSchedule&amp;quot;: false,&lt;br /&gt;
         &amp;quot;updateAttributes&amp;quot;: false,&lt;br /&gt;
         &amp;quot;jobSchedules&amp;quot;: [],&lt;br /&gt;
         &amp;quot;jobClass&amp;quot;: &amp;quot;com.carfey.ops.job.script.GroovyJob&amp;quot;,&lt;br /&gt;
         &amp;quot;nickname&amp;quot;: &amp;quot;Script Job&amp;quot;,&lt;br /&gt;
         &amp;quot;pickupBufferMinutes&amp;quot;: 2,&lt;br /&gt;
         &amp;quot;recoveryType&amp;quot;: &amp;quot;NONE&amp;quot;,&lt;br /&gt;
         &amp;quot;hosts&amp;quot;: [],&lt;br /&gt;
         &amp;quot;ordinalParameters&amp;quot;: [&lt;br /&gt;
           &amp;quot;ordinal&amp;quot;: 0,&lt;br /&gt;
           &amp;quot;parameters&amp;quot;: [&lt;br /&gt;
              {&lt;br /&gt;
                 &amp;quot;name&amp;quot;: &amp;quot;script&amp;quot;,&lt;br /&gt;
                 &amp;quot;type&amp;quot;: &amp;quot;STRING&amp;quot;,&lt;br /&gt;
                 &amp;quot;value&amp;quot;: &amp;quot;sdfds&amp;quot;&lt;br /&gt;
              }&lt;br /&gt;
           ]&lt;br /&gt;
         ],&lt;br /&gt;
         &amp;quot;chainAll&amp;quot;: false,&lt;br /&gt;
         &amp;quot;hostPreference&amp;quot;: false,&lt;br /&gt;
         &amp;quot;autoRetryCount&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryInterval&amp;quot;: 0,&lt;br /&gt;
         &amp;quot;autoRetryIntervalExponent&amp;quot;: false,&lt;br /&gt;
         &amp;quot;state&amp;quot;: &amp;quot;DISABLED&amp;quot;,&lt;br /&gt;
         &amp;quot;effectiveDate&amp;quot;: &amp;quot;2015-01-01T00:00:00-0700&amp;quot;&lt;br /&gt;
         &amp;quot;endDate&amp;quot;: &amp;quot;2999-12-31T23:59:00-0800&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
   ],&lt;br /&gt;
   &amp;quot;globalParameters&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;name&amp;quot;: &amp;quot;dbPassword&amp;quot;,&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;STRING&amp;quot;,&lt;br /&gt;
         &amp;quot;values&amp;quot;: [&amp;quot;secret&amp;quot;]&lt;br /&gt;
      }&lt;br /&gt;
   ],&lt;br /&gt;
   &amp;quot;users&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;update&amp;quot;: false,&lt;br /&gt;
         &amp;quot;userName&amp;quot;: &amp;quot;admin&amp;quot;,&lt;br /&gt;
         &amp;quot;active&amp;quot;: true,&lt;br /&gt;
         &amp;quot;roles&amp;quot;: [&lt;br /&gt;
            &amp;quot;API&amp;quot;,&lt;br /&gt;
            &amp;quot;ADMIN&amp;quot;,&lt;br /&gt;
            &amp;quot;WRITE&amp;quot;&lt;br /&gt;
         ],&lt;br /&gt;
         &amp;quot;password&amp;quot;: &amp;quot;changeme&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
   ],&lt;br /&gt;
   &amp;quot;chains&amp;quot;: {&lt;br /&gt;
      &amp;quot;replaceAll&amp;quot;: false,&lt;br /&gt;
      &amp;quot;items&amp;quot;: [&lt;br /&gt;
         {&lt;br /&gt;
            &amp;quot;sourceJobNickname&amp;quot;: &amp;quot;Script Job&amp;quot;,&lt;br /&gt;
            &amp;quot;targetJobNickname&amp;quot;: &amp;quot;Log Cleanup&amp;quot;,&lt;br /&gt;
            &amp;quot;active&amp;quot;: true,&lt;br /&gt;
            &amp;quot;triggerStates&amp;quot;: [&lt;br /&gt;
               &amp;quot;FAILED&amp;quot;,&lt;br /&gt;
               &amp;quot;CONDITIONAL&amp;quot;&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;resultConditions&amp;quot;: [&lt;br /&gt;
               {&lt;br /&gt;
                  &amp;quot;variableName&amp;quot;: &amp;quot;dfdsfds&amp;quot;,&lt;br /&gt;
                  &amp;quot;operator&amp;quot;: &amp;quot;EQUALS&amp;quot;,&lt;br /&gt;
                  &amp;quot;values&amp;quot;: [&lt;br /&gt;
                     &amp;quot;sdfs&amp;quot;&lt;br /&gt;
                  ]&lt;br /&gt;
               }&lt;br /&gt;
            ]&lt;br /&gt;
         }&lt;br /&gt;
      ]&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;conflicts&amp;quot;: [&lt;br /&gt;
      [&lt;br /&gt;
         &amp;quot;Script Job&amp;quot;,&lt;br /&gt;
         &amp;quot;Log Cleanup&amp;quot;&lt;br /&gt;
      ]&lt;br /&gt;
   ],&lt;br /&gt;
   &amp;quot;customCalendars&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;name&amp;quot;: &amp;quot;Sample Calendar&amp;quot;,&lt;br /&gt;
         &amp;quot;dates&amp;quot;: [&lt;br /&gt;
            &amp;quot;2011-01-01&amp;quot;&lt;br /&gt;
         ]&lt;br /&gt;
      }&lt;br /&gt;
   ],&lt;br /&gt;
   &amp;quot;systemParameters&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;name&amp;quot;: &amp;quot;adHocJobsRespectFixedHostsRestrictions&amp;quot;,&lt;br /&gt;
         &amp;quot;description&amp;quot;: &amp;quot;This value determines whether the Fixed Hosts restriction assigned to a Job is respected for Ad Hoc jobs.&amp;quot;,&lt;br /&gt;
         &amp;quot;category&amp;quot;: &amp;quot;JOB&amp;quot;,&lt;br /&gt;
         &amp;quot;value&amp;quot;: &amp;quot;true&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
   ],&lt;br /&gt;
   &amp;quot;templates&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;jobNicknames&amp;quot;: [],&lt;br /&gt;
         &amp;quot;name&amp;quot;: &amp;quot;Obsidian Default Job Template&amp;quot;,&lt;br /&gt;
         &amp;quot;active&amp;quot;: true,&lt;br /&gt;
         &amp;quot;category&amp;quot;: &amp;quot;JOB&amp;quot;,&lt;br /&gt;
         &amp;quot;defaultForJobs&amp;quot;: true,&lt;br /&gt;
         &amp;quot;subjectTemplate&amp;quot;: &amp;quot;Obsidian [{{hostName}}] {{subject}}&amp;quot;,&lt;br /&gt;
         &amp;quot;bodyTemplate&amp;quot;: &amp;quot;Body Template&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
   ],&lt;br /&gt;
   &amp;quot;subscribers&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;generalSubscriptions&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
               &amp;quot;jobNickname&amp;quot;: &amp;quot;Log Cleanup&amp;quot;,&lt;br /&gt;
               &amp;quot;category&amp;quot;: &amp;quot;JOB&amp;quot;,&lt;br /&gt;
               &amp;quot;level&amp;quot;: &amp;quot;ERROR&amp;quot;,&lt;br /&gt;
               &amp;quot;active&amp;quot;: true&lt;br /&gt;
            }&lt;br /&gt;
         ],&lt;br /&gt;
         &amp;quot;jobExecutionSubscriptions&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
               &amp;quot;jobNicknames&amp;quot;: [],&lt;br /&gt;
               &amp;quot;allJobs&amp;quot;: true,&lt;br /&gt;
               &amp;quot;triggerStates&amp;quot;: [&lt;br /&gt;
                  &amp;quot;CONDITIONAL&amp;quot;,&lt;br /&gt;
                  &amp;quot;DIED&amp;quot;,&lt;br /&gt;
                  &amp;quot;FAILED&amp;quot;,&lt;br /&gt;
                  &amp;quot;RECOVERY&amp;quot;&lt;br /&gt;
               ],&lt;br /&gt;
               &amp;quot;resultConditions&amp;quot;: [&lt;br /&gt;
                  {&lt;br /&gt;
                     &amp;quot;variableName&amp;quot;: &amp;quot;someVar&amp;quot;,&lt;br /&gt;
                     &amp;quot;operator&amp;quot;: &amp;quot;EQUALS&amp;quot;,&lt;br /&gt;
                     &amp;quot;values&amp;quot;: [&lt;br /&gt;
                        &amp;quot;someValue&amp;quot;&lt;br /&gt;
                     ]&lt;br /&gt;
                  }&lt;br /&gt;
               ],&lt;br /&gt;
               &amp;quot;active&amp;quot;: true&lt;br /&gt;
            }&lt;br /&gt;
         ],&lt;br /&gt;
         &amp;quot;emailAddress&amp;quot;: &amp;quot;test@example.com&amp;quot;,&lt;br /&gt;
         &amp;quot;active&amp;quot;: true&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Implementing_Jobs&amp;diff=4148</id>
		<title>Implementing Jobs</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Implementing_Jobs&amp;diff=4148"/>
		<updated>2026-07-31T14:49:53Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This information covers implementing [[Admin_Jobs|jobs]] in Java. This includes how to write your own jobs, use parameterization and job result features, and how to set up your classpath to include your own job implementations. If you want to schedule execution of scripts, please see our [[Scripting Jobs]] topic.&lt;br /&gt;
&lt;br /&gt;
We recommend you review this page fully before implementing your own jobs. Obsidian provides you features that are not available in other schedulers which greatly improve re-usability and help ensure reliable execution. Reviewing this page and considering all available features will help you make the best choices for your needs.&lt;br /&gt;
&lt;br /&gt;
You can also look at examples in our convenience [[Built-in_Jobs|Built-in Jobs]] that have been open-sourced under the [[https://opensource.org/license/mit/ MIT License]]. In the root of the installation folder, you can find the source in &amp;lt;code&amp;gt;obsidian-builtin-job-src.jar&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In addition, you can check out our [https://web.obsidianscheduler.com/obsidianapi/ Javadoc] which documents the features you&#039;ll need to write your own Obsidian jobs. We recommend you consult with the Javadoc in combination with this page to understand the best way to use Obsidian&#039;s job functionality.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= SchedulableJob Interface =&lt;br /&gt;
&lt;br /&gt;
[https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/SchedulableJob.html SchedulableJob Javadoc]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; If you need to set up a development environment to create Obsidian jobs, see the [[Implementing_Jobs#Classpath_for_Building|Classpath]] section.&lt;br /&gt;
&lt;br /&gt;
Implementing jobs in Obsidian is very straightforward for most cases. At its most basic, implementing a job simply requires implementing the &amp;lt;code&amp;gt;SchedulableJob&amp;lt;/code&amp;gt; interface which has a single method, as shown below.&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public interface SchedulableJob {&lt;br /&gt;
      public void execute(Context context) throws Exception;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In your implementation, the &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; method does any work required in the job and it can throw any type of Exception, which is handled automatically by Obsidian. &lt;br /&gt;
&lt;br /&gt;
If you aren&#039;t using parameterization or saving job results, that&#039;s all you need to do. It&#039;s likely you&#039;ll just be calling some existing code through your job implementation. Here&#039;s an example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import com.carfey.ops.job.Context;&lt;br /&gt;
import com.carfey.ops.job.SchedulableJob;&lt;br /&gt;
import com.carfey.ops.job.param.Description;&lt;br /&gt;
&lt;br /&gt;
@Description(&amp;quot;This helpful description will show in the job configuration screen.&amp;quot;)&lt;br /&gt;
public class MyScheduledJob implements SchedulableJob {&lt;br /&gt;
	public void execute(Context context) throws Exception {&lt;br /&gt;
		CatalogExporter exporter = new CatalogExporter ();&lt;br /&gt;
		exporter.export();&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All executed jobs are supplied a &amp;lt;code&amp;gt;Context&amp;lt;/code&amp;gt; object (see [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/Context.html Javadoc]) is used to expose configuration parameters and job results. &lt;br /&gt;
&lt;br /&gt;
You can also access the scheduled runtime of the job using &amp;lt;code&amp;gt;com.carfey.jdk.lang.DateTime Context.getScheduledTime()&amp;lt;/code&amp;gt;. If you wish to convert this to another Date type, such as &amp;lt;code&amp;gt;java.util.Date&amp;lt;/code&amp;gt;, you can use the &amp;lt;code&amp;gt;getMillis()&amp;lt;/code&amp;gt; method which provides UTC time in milliseconds from the epoch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Date runTime = new java.util.Date(context.getScheduledTime().getMillis());&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; You can annotate your job with the &amp;lt;code&amp;gt;com.carfey.ops.job.param.Description&amp;lt;/code&amp;gt; (see [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/param/Description.html Javadoc]) annotation to provide a helpful job description which is shown in the job configuration screen. This can be useful for indicating how a job should be configured. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 4.3.0&#039;&#039;&#039;, descriptions support [[Description_%26_Parameter_Formatting | formatting]] for rendering in the UI.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 4.9.0&#039;&#039;&#039;, Description supports providing an optional array of &amp;lt;code&amp;gt;urls&amp;lt;/code&amp;gt;. Any provided URLs are displayed in the UI&#039;s inline help for the job. This is useful for linking to additional documentation regarding your job class.&lt;br /&gt;
&lt;br /&gt;
== Async Jobs ==&lt;br /&gt;
&lt;br /&gt;
[https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/SchedulableJob.AsyncJob.html AsyncJob Javadoc]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 4.5.0&#039;&#039;&#039;, this marker annotation is used to indicate that a SchedulableJob kicks off an asynchronous process, such as a call to a web service. Once the asynchronous process has been completed, the &amp;lt;code&amp;gt;execute(Context context)&amp;lt;/code&amp;gt; method should complete. The job will then be marked as &amp;lt;code&amp;gt;PENDING&amp;lt;/code&amp;gt; indicating that its ultimate status is pending. The activity record will remain in this state until either the [[REST_Endpoints#POST_async_results|REST API]] or [[Embedded_API#Post_Results_to_a_Pending_.28Async.29_Runtime|Embedded API]] is used to set the final results of the job.&lt;br /&gt;
&lt;br /&gt;
== Threading in Jobs ==&lt;br /&gt;
&lt;br /&gt;
Obsidian runs each job within its own Java thread, so execution does not hold up scheduling or execution of any other job. For most jobs, you can do all your work in this single thread and not worry about multi-threading or thread safety.&lt;br /&gt;
&lt;br /&gt;
However, you may wish to multi-thread execution within a job for performance reasons. There is no problem with this approach, but to do so, there are a few things to keep in mind:&lt;br /&gt;
* If you start any threads or create an &amp;lt;code&amp;gt;ExecutorService&amp;lt;/code&amp;gt;, you will need to clean up before the job terminates. All resources acquired within the &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; method must be cleaned up by your job code, including threads.&lt;br /&gt;
* If you want the job to terminate only after all threads or created tasks complete, you must call &amp;lt;code&amp;gt;Thread.join()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Future.get()&amp;lt;/code&amp;gt;, or an equivalent method to ensure execution completes before the job thread continues and exits the &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; method.&lt;br /&gt;
* For job failures within created threads or tasks to be reported to Obsidian, an &amp;lt;code&amp;gt;Exception&amp;lt;/code&amp;gt; must be thrown by the job thread itself. For example, this can be done via &amp;lt;code&amp;gt;Future.get()&amp;lt;/code&amp;gt;. For raw threads, you may need to save some state indicating success or failure, and throw an &amp;lt;code&amp;gt;Exception&amp;lt;/code&amp;gt; as appropriate within the job&#039;s execution thread.&lt;br /&gt;
&lt;br /&gt;
= Dependency Injection via Spring =&lt;br /&gt;
Obsidian supports executing jobs wired as components via Spring. See our dedicated page on [[Spring_Integration|Spring Integration]] for full details.&lt;br /&gt;
&lt;br /&gt;
= Parameterization =&lt;br /&gt;
&lt;br /&gt;
Obsidian offers flexibility and reuse in your jobs by supplying configurable parameters for each job.&lt;br /&gt;
&lt;br /&gt;
If you would like to parameterize jobs, you can define parameters on the job class itself, or use custom parameters which are only set when configuring a job. Defined parameters are automatically displayed in the [[Admin_Jobs|Jobs]] screen to help guide configuration, but also to provide defaults and enforce data types and required values. Custom parameters can be set for any job, and lack additional validation.&lt;br /&gt;
&lt;br /&gt;
Defined parameters are specified on the job class using the &amp;lt;code&amp;gt;@Configuration&amp;lt;/code&amp;gt; annotation (see [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/param/Configuration.html Javadoc]). &lt;br /&gt;
&lt;br /&gt;
The following example shows a job using various parameters. It includes a required &#039;&#039;url&#039;&#039; parameter has two valid values, an optional set of names for saving the results and a Boolean value to determine whether compression should be used. It shows a fairly comprehensive usage of various data types and other parameter settings.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import com.carfey.ops.job.param.Configuration;&lt;br /&gt;
import com.carfey.ops.job.param.Parameter;&lt;br /&gt;
import com.carfey.ops.job.param.Type;&lt;br /&gt;
&lt;br /&gt;
@Configuration(knownParameters={&lt;br /&gt;
		@Parameter(name=&amp;quot;url&amp;quot;, required=true, type=Type.STRING, listArgs={&amp;quot;https://google.com&amp;quot;,&amp;quot;https://obsidianscheduler.com&amp;quot;}),&lt;br /&gt;
		@Parameter(name=&amp;quot;saveResultsParam&amp;quot;, required=false, allowMultiple=true, type=Type.STRING),&lt;br /&gt;
		@Parameter(name=&amp;quot;compressResults&amp;quot;, required=false, defaultValue=&amp;quot;false&amp;quot;, type=Type.BOOLEAN)&lt;br /&gt;
	})&lt;br /&gt;
public class MyScheduledJob implements SchedulableJob {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 4.3.0&#039;&#039;&#039;, Parameter descriptions support [[Description_%26_Parameter_Formatting | formatting]] for rendering in the UI.&lt;br /&gt;
&lt;br /&gt;
If you are running parameterized jobs, these parameters are very easy to access.  Both defined and custom parameters are accessed in the same way.  Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void execute(Context context) throws Exception {&lt;br /&gt;
	JobConfig config = context.getConfig();&lt;br /&gt;
&lt;br /&gt;
	MyExistingFunction function = new MyExistingFunction();&lt;br /&gt;
&lt;br /&gt;
	String url = config.getString(&amp;quot;url&amp;quot;);&lt;br /&gt;
	function.setUrl(url);&lt;br /&gt;
&lt;br /&gt;
        boolean compress = config.getBoolean(&amp;quot;compressResults&amp;quot;); // defaults to false&lt;br /&gt;
        function.setCompress(compress);&lt;br /&gt;
	&lt;br /&gt;
        String result = function.go();&lt;br /&gt;
        &lt;br /&gt;
        for (String resultsName : config.getStringList(&amp;quot;saveResultsParam&amp;quot;)) {&lt;br /&gt;
             context.saveJobResult(resultsName, result);&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For all the available methods on &amp;lt;code&amp;gt;JobConfig&amp;lt;/code&amp;gt;, see the [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/config/JobConfig.html Javadoc].&lt;br /&gt;
&lt;br /&gt;
The following is the &amp;lt;code&amp;gt;@Parameter&amp;lt;/code&amp;gt; source code (see [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/param/Parameter.html Javadoc]), which helps illustrate attributes that can be configured:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public @interface Parameter {&lt;br /&gt;
	public String name();&lt;br /&gt;
	public boolean required();&lt;br /&gt;
        public boolean requiredAtRuntime();&lt;br /&gt;
	public Type type() default Type.STRING;&lt;br /&gt;
	public boolean allowMultiple() default false;&lt;br /&gt;
	public String defaultValue() default &amp;quot;&amp;quot;;&lt;br /&gt;
        public Class&amp;lt;? extends ListProvider&amp;gt; listProvider() default StaticListProvider.class;&lt;br /&gt;
	public String[] listArgs() default {};&lt;br /&gt;
	public String description() default &amp;quot;&amp;quot;; // as of 4.0.2&lt;br /&gt;
	public String[] urls() default {}; //as of 4.9.0&lt;br /&gt;
	public boolean requireUniqueSelection() default false; //as of 4.10.0&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;As of Obsidian 4.0.2&#039;&#039;, a parameter can be associated with a &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt; that is integrated with help information displayed in the user interface. This description is also returned in the API calls that return job parameter information.&lt;br /&gt;
&lt;br /&gt;
A parameter can be defined as &amp;lt;code&amp;gt;requiredAtRuntime&amp;lt;/code&amp;gt;. This allows the job to be configured without a parameter, but ensures a parameter value is set with one-time submissions. Of course, if it is configured with a parameter value, one-time submissions will not require a value.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;As of Obsidian 4.9.0&#039;&#039;, Parameters support providing an optional array of &amp;lt;code&amp;gt;urls&amp;lt;/code&amp;gt;. Any provided URLs are displayed in the UI&#039;s inline help for the parameter. This is useful for linking to additional documentation regarding your parameter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;As of Obsidian 4.10.0&#039;&#039;, Parameters support validation of multiple values to be unique via &amp;lt;code&amp;gt;boolean requireUniqueSelection&amp;lt;/code&amp;gt; which defaults to false. Only in play when &amp;lt;code&amp;gt;allowMultiple&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== List Parameterization ==&lt;br /&gt;
&lt;br /&gt;
You can directly specify a list of valid values within a &amp;lt;code&amp;gt;@Parameter&amp;lt;/code&amp;gt; annotation by using the &amp;lt;code&amp;gt;listArgs&amp;lt;/code&amp;gt; option. The [[Admin_Jobs|job screen]] will then present the values for these parameters as a selection list. Note that if &amp;lt;code&amp;gt;required&amp;lt;/code&amp;gt; is set to false, an empty value will automatically be included in the list.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@Configuration(knownParameters={&lt;br /&gt;
    @Parameter(name=&amp;quot;logLevel&amp;quot;, required=true, type=Type.STRING, listArgs={&amp;quot;ERROR&amp;quot;, &amp;quot;INFO&amp;quot;, &amp;quot;DEBUG&amp;quot;})&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== List Providers ===&lt;br /&gt;
&lt;br /&gt;
For more complex scenarios, you may wish to enumerate values through code. This can be done by creating your own implementation of the [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/param/ListProvider.html ListProvider] interface, including it in the Obsidian classpath, and then referencing it in your &amp;lt;code&amp;gt;@Parameter&amp;lt;/code&amp;gt; annotation via &amp;lt;code&amp;gt;listProvider&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;listArgs&amp;lt;/code&amp;gt; value can be used to provide arguments to your &amp;lt;code&amp;gt;listProvider&amp;lt;/code&amp;gt;, since they are passed into it when enumerating valid values.&lt;br /&gt;
&lt;br /&gt;
The example below demonstrates this using the built-in [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/param/FileListProvider.html FileListProvider], which provides a listing of full file paths based on a directory configured in a global parameter specified via &amp;lt;code&amp;gt;listArgs&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/** Enumerate all files in the directory specified by the global parameter &amp;quot;rootDirectory&amp;quot;. **/&lt;br /&gt;
@Configuration(knownParameters={&lt;br /&gt;
    @Parameter(name=&amp;quot;file&amp;quot;, required=true, type=Type.STRING, listProvider=com.carfey.ops.job.param.FileListProvider.class, listArgs={&amp;quot;rootDirectory&amp;quot;})&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Dynamic List Providers ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;As of Obsidian 4.5.0&#039;&#039;, a &amp;lt;code&amp;gt;ListProvider&amp;lt;/code&amp;gt; can be marked as providing dynamic, or changing values, using the [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/param/ListProvider.DynamicListProvider.html DynamicListProvider] annotation. By doing so, the provider will be asked to provide its valid values every time the [[Implementing_Jobs#Classpath_Scanning | Classpath Scanner]] runs. Any configured jobs will need to satisfy the values when they are executed or re-configured.&lt;br /&gt;
&lt;br /&gt;
=== Dynamic File Lists ===&lt;br /&gt;
&lt;br /&gt;
If you wish to define a parameter which enumerates a file listing based on a server-side directory, you can use the built-in [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/param/FileListProvider.html FileListProvider].  It also marked with the [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/param/ListProvider.DynamicListProvider.html DynamicListProvider] annotation explained below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This allows you to enumerate files in a server-side directory which is configured in a named [[Admin_Global_Parameters|global parameter]]. To use this feature, specify the appropriate &amp;lt;code&amp;gt;listProvider&amp;lt;/code&amp;gt; class along with at least one value for &amp;lt;code&amp;gt;listArgs&amp;lt;/code&amp;gt; to specify the global parameter name which will contain the configured directory. When the job is configured, Obsidian will enumerate valid values from the directory configured in the global parameter. At execution, the configured value will also be checked to ensure it is a valid value based on the current directory listing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Configuration(knownParameters={&lt;br /&gt;
   @Parameter(name=&amp;quot;fileToProcess&amp;quot;, type=Type.STRING, listArgs={&amp;quot;sourceDirectory&amp;quot;}, listProvider=FileListProvider.class, required = false),&lt;br /&gt;
   @Parameter(name=&amp;quot;logTarget&amp;quot;, type=Type.STRING, listArgs={&amp;quot;logDirectory&amp;quot;, &amp;quot;false&amp;quot;, &amp;quot;.*log&amp;quot;, &amp;quot;true&amp;quot;}, listProvider=FileListProvider.class, required = false)&lt;br /&gt;
})&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As shown in the &amp;lt;code&amp;gt;logTarget&amp;lt;/code&amp;gt; parameter, &amp;lt;code&amp;gt;FileListProvider&amp;lt;/code&amp;gt; supports additional arguments. See the [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/param/FileListProvider.html Javadoc] for full usage details.&lt;br /&gt;
&lt;br /&gt;
== Inheritance ==&lt;br /&gt;
&lt;br /&gt;
By default, all &amp;lt;code&amp;gt;@Configuration&amp;lt;/code&amp;gt; annotations on the job class hierarchy are inherited by children and their parameters are combined. However, if a subclass defines a parameter with the same name as a parent class, the subclass version will override the parent version.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;@Configuration&amp;lt;/code&amp;gt; annotation supports a &amp;lt;code&amp;gt;replaceInherited&amp;lt;/code&amp;gt; attribute. If set to true, parent classes&#039; &amp;lt;code&amp;gt;@Configuration&amp;lt;/code&amp;gt; annotations are completely ignored, effectively replacing their parameter definitions completely.&lt;br /&gt;
&lt;br /&gt;
== Global Parameters ==&lt;br /&gt;
&lt;br /&gt;
Obsidian supports [[Admin_Global_Parameters|Global Parameters]]. These let you configure job parameters globally, and then simply import them into jobs as needed. Global parameters help avoid repeating the same configuration steps over and over, and can even be used to hide sensitive values from users, since they have separate access control in the admin web application.&lt;br /&gt;
&lt;br /&gt;
By default, if a job parameter is configured with a value that is surrounded by double curly braces (e.g. &amp;lt;code&amp;gt;&amp;amp;#123;&amp;amp;#123;param&amp;amp;#125;&amp;amp;#125;&amp;lt;/code&amp;gt;), then it is treated as a global parameter reference. When Obsidian sees a global parameter reference in this format during job execution, it imports all configured global parameters under the name (e.g. &amp;lt;code&amp;gt;param&amp;lt;/code&amp;gt;) in place of the reference. Note that Obsidian does not support global parameter references embedded inside parameter values, since it does not perform text substitution - only parameter values containing only the global parameter reference will be replaced with the global parameter value.&lt;br /&gt;
&lt;br /&gt;
Obsidian will perform automatic type conversion for all values - a global parameter&#039;s type definition doesn&#039;t have to match the type of the  defined parameter that references it. Once Obsidian has resolved all global parameter values, it will validate them to ensure all defined parameter restrictions are respected. Note that Obsidian strictly enforces that a global parameter must exist when referenced.&lt;br /&gt;
&lt;br /&gt;
Note that you can configure a job parameter with multiple global parameter references along with normal values, and Obsidian will combine them all into the configuration passed into your job.&lt;br /&gt;
&lt;br /&gt;
The [[Admin_Global_Parameters|Global Parameters]] page explains how to configure global parameters.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; If you wish to change the tokens used to surround global parameters, you may override them using properties outlined in [[Advanced Configuration]].&lt;br /&gt;
&lt;br /&gt;
=== Global Substitution Mode ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In some cases, you may wish to embed global parameters inside other parameters, rather than substitute them entirely. For example, when using a [[Built-in_Jobs#Script_Job|ScriptFileJob]], you may wish to inject a global parameter value into an argument passed into a script as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-Dfile.encoding=&amp;amp;#123;&amp;amp;#123;globalFileEncoding}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To enable this, update the [[Admin_Scheduler_Settings|scheduler setting]] &amp;lt;code&amp;gt;useGlobalSubstitutions&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;. Note that this changes the behaviour of &#039;&#039;&#039;all global parameter references&#039;&#039;&#039; to use plain text substitution.&lt;br /&gt;
&lt;br /&gt;
After enabling this setting, you may reference any number of global parameters inside job parameters using the normal curly brace syntax (e.g. &amp;lt;code&amp;gt;&amp;amp;#123;&amp;amp;#123;globalParamName&amp;amp;#125;&amp;amp;#125;&amp;lt;/code&amp;gt;), and they may occur anywhere in the parameter value.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important Note:&#039;&#039;&#039; Changing this setting may impact existing jobs since global substitutions use the first configured global parameter value to perform text substitution, while the normal behaviour expands global parameter references to use all configured values. In addition, if any job parameters contain text within doubled-up curly braces, Obsidian will interpret these as global parameter references and will fail job validation if they do not exist.&lt;br /&gt;
&lt;br /&gt;
== Ad Hoc &amp;amp; One-Time Run Parameters ==&lt;br /&gt;
&lt;br /&gt;
In addition to defining parameters for at the job level, Obsidian supports accepting parameters for a specific run time (i.e. job history) through the [[Admin_Jobs|Jobs]] screen, or via the [[REST_API|REST]] or [[Embedded_API|Embedded]] APIs. If a parameter name for a run parameter has the same name as a configured job parameter, the job parameter values are dropped, and the run parameter values are used instead.&lt;br /&gt;
&lt;br /&gt;
These parameters are treated the same as those at the job level, and are exposed to the job in the same manner as parameters at the job level. Note that parameters must have the same data type as any already configured for the job, and must conform to restrictions defined by the &amp;lt;code&amp;gt;@Configuration&amp;lt;/code&amp;gt; annotation if applicable.&lt;br /&gt;
&lt;br /&gt;
= Config Validating Job =&lt;br /&gt;
&lt;br /&gt;
[https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/ConfigValidatingJob.html ConfigValidatingJob Javadoc]&lt;br /&gt;
&lt;br /&gt;
In addition to providing simple validation mechanisms through the &amp;lt;code&amp;gt;@Parameter&amp;lt;/code&amp;gt; annotation, Obsidian gives you a way to add custom parameter validation to a job.&lt;br /&gt;
&lt;br /&gt;
The interface &amp;lt;code&amp;gt;com.carfey.ops.job.ConfigValidatingJob&amp;lt;/code&amp;gt; extends &amp;lt;code&amp;gt;SchedulableJob&amp;lt;/code&amp;gt; and allows you to provide additional parameter validation that goes beyond type validity and mandatory values. Below is its definition:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public interface ConfigValidatingJob extends SchedulableJob {	&lt;br /&gt;
&lt;br /&gt;
	public void validateConfig(JobConfig config) throws ValidationException, ParameterException;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a job implementing this interface is configured or executed, the &amp;lt;code&amp;gt;validateConfig()&amp;lt;/code&amp;gt; method is called. All configured parameters are available in the same &amp;lt;code&amp;gt;JobConfig&amp;lt;/code&amp;gt; object that is provided to the &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; method.  You can perform any validation you require within this method.  If validation fails, the job will not be created, modified or executed (depending on when validation fails), and the messages you added to the &amp;lt;code&amp;gt;ValidationException&amp;lt;/code&amp;gt; are displayed to the user.  Consider this example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void validateConfig(JobConfig config) throws ValidationException, ParameterException {&lt;br /&gt;
	List&amp;lt;String&amp;gt; hosts = config.getStringList(&amp;quot;hosts&amp;quot;);&lt;br /&gt;
	ValidationException ve = new ValidationException();&lt;br /&gt;
	if (hosts.size() &amp;lt; 2) {&lt;br /&gt;
		ve.add(&amp;quot;Host syncronization job requires at least two hosts to synchronize.&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
	int timeout = config.getInt(&amp;quot;timeout&amp;quot;);&lt;br /&gt;
	if (timeout &amp;lt; 0) {&lt;br /&gt;
		ve.add(String.format(&amp;quot;Timeout must be 0 indicating no timeout or greater than 0 to indicate timeout duration.  Timeout provided was %s.&amp;quot;, timeout));&lt;br /&gt;
	}&lt;br /&gt;
	if (!ve.getMessages().isEmpty()) {&lt;br /&gt;
		throw ve;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Validation on Non-Scheduler Instances ===&lt;br /&gt;
If you configure a &amp;lt;code&amp;gt;ConfigValidatingJob&amp;lt;/code&amp;gt; on a non-scheduler web application which does not have the job classpath available, Obsidian is forced to skip calling the corresponding validation method when the job is saved, but it will still do so during execution.&lt;br /&gt;
&lt;br /&gt;
= Job Results =&lt;br /&gt;
&lt;br /&gt;
Obsidian also allows for storing information about your job execution. This information is then available in chained and resubmitted jobs. In addition, as of release 1.4, jobs can be conditionally chained based on the saved results of a completed trigger job. &lt;br /&gt;
&lt;br /&gt;
Job Results can be viewed after a job completes in the [[Admin_Job_Activity#Job_Activity_Details|Job Activity]] screen. They are also exposed in the Obsidian [[REST_API|REST API]].&lt;br /&gt;
&lt;br /&gt;
Note this example that both evaluates source job information (i.e. job results saved by the job that chained to this one) and saves state from its own execution which could be used by a subsequently chained job:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public void execute(Context context) throws Exception {&lt;br /&gt;
	Map&amp;lt;String, List&amp;lt;Object&amp;gt;&amp;gt; sourceJobResults = context.getSourceJobResults();&lt;br /&gt;
	&lt;br /&gt;
        // Grab results from the source job that was chained to this one&lt;br /&gt;
        List&amp;lt;Object&amp;gt; oldResultsList = sourceJobResults.get(&amp;quot;inputFile&amp;quot;);&lt;br /&gt;
	String oldResults = (String) oldResultsList.get(0);&lt;br /&gt;
&lt;br /&gt;
	... job execution ...&lt;br /&gt;
&lt;br /&gt;
        // This saved value is then available to chained jobs and can be viewed in the UI&lt;br /&gt;
	context.saveJobResult(resultsParamName, oldResults + &amp;quot; Updated&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        // You can save multiple results at a time as a convenience.&lt;br /&gt;
	context.saveMultipleJobResults(&amp;quot;file&amp;quot;, Arrays.asList(&amp;quot;first&amp;quot;, &amp;quot;second&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
        // You can replace job results.&lt;br /&gt;
	context.replaceJobResult(resultsParamName, &amp;quot;replace old value&amp;quot;);&lt;br /&gt;
        context.replaceMultipleJobResults(&amp;quot;file&amp;quot;, Arrays.asList(&amp;quot;third&amp;quot;, &amp;quot;fourth&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;Context&amp;lt;/code&amp;gt; object (see [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/Context.html Javadoc]) methods used for retrieving and storing results are:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;java.util.Map&amp;lt;java.lang.String,java.util.List&amp;lt;java.lang.Object&amp;gt;&amp;gt; getSourceJobResults()&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;void saveJobResult(java.lang.String name, java.lang.Object value)&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;void saveMultipleJobResults(java.lang.String name, Collection&amp;lt;?&amp;gt; values)&amp;lt;/code&amp;gt; (from 2.2 onward)&lt;br /&gt;
* &amp;lt;code&amp;gt;void replaceJobResult(java.lang.String name, java.lang.Object value)&amp;lt;/code&amp;gt; (from 3.6 onward)&lt;br /&gt;
* &amp;lt;code&amp;gt;void replaceMultipleJobResults(java.lang.String name, Collection&amp;lt;?&amp;gt; values)&amp;lt;/code&amp;gt; (from 3.6 onward)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note that &amp;lt;code&amp;gt;getSourceJobResults()&amp;lt;/code&amp;gt; will return job results saved by the job that was chained directly to the currently executing job. If multiple jobs are chained in sequence, this method will &#039;&#039;not&#039;&#039; return results from every job in the chain. If you wish to pass all results down the chain, you can invoke &amp;lt;code&amp;gt;saveMultipleJobResults()&amp;lt;/code&amp;gt; within each job, using the values from &amp;lt;code&amp;gt;getSourceJobResults()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Default Supported Job Result Types ==&lt;br /&gt;
Though the job result methods accept &amp;lt;code&amp;gt;java.lang.Object&amp;lt;/code&amp;gt;, by default there are limitations to what types Obsidian can store as a job result:&lt;br /&gt;
* Basic &amp;lt;code&amp;gt;java.lang&amp;lt;/code&amp;gt; types such as &amp;lt;code&amp;gt;Boolean&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; and subclasses of &amp;lt;code&amp;gt;Number&amp;lt;/code&amp;gt; are supported automatically and stored using &amp;lt;code&amp;gt;toString()&amp;lt;/code&amp;gt; representations.&lt;br /&gt;
* If a type has a public constructor that accepts a single &amp;lt;code&amp;gt;String&amp;lt;/code&amp;gt; argument, its &amp;lt;code&amp;gt;toString()&amp;lt;/code&amp;gt; representation is used to store the result, and the constructor is used to re-construct it.&lt;br /&gt;
* &#039;&#039;As of Obsidian 4.4&#039;&#039;, all other types are stored using the object&#039;s JSON representation by using [https://github.com/google/gson GSON]. &lt;br /&gt;
* If you need to add support for types that do not work with the default implementation, or wish to completely change how types are stored and re-constructed, see the next section.&lt;br /&gt;
&lt;br /&gt;
== Customizing Job Result Serialization and Deserialization ==&lt;br /&gt;
&lt;br /&gt;
You may wish to customize the storage (serialization) and re-construction (deserialization) of job results, either to get custom behaviour or to support types that do not work by default. For example, you may wish to use Jackson-based JSON  for complex types instead of the default GSON approach.&lt;br /&gt;
&lt;br /&gt;
To customize behaviour, you may implement your own [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/result/SerializationStrategy.html SerializationStategy] and register it via the [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/result/SerializationFactory.html SerializationFactory] singleton. The linked javadoc contains all the information you need to understand Obsidian&#039;s default behaviour and how to customize it.&lt;br /&gt;
&lt;br /&gt;
Below is a sample that maintains Obsidian&#039;s default serialization for simple types, but uses a custom [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/result/SerializationStrategy.html SerializationStategy] for complex types.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
List&amp;lt;SerializationStrategy&amp;gt; strategies = Arrays.asList(&lt;br /&gt;
      new DefaultSerializationStrategy(),     // maintain Obsidian&#039;s simple type and String-based constructor support as the highest priority strategy&lt;br /&gt;
      new CustomTypeSerializationStrategy()   // add our custom strategy for all other types&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
SerializationFactory singletonFactory = SerializationFactory.get();&lt;br /&gt;
singletonFactory.replaceAll(strategies);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Annotation-Based Jobs =&lt;br /&gt;
&lt;br /&gt;
[https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/SchedulableJob.Schedulable.html Schedulable Javadoc]&lt;br /&gt;
&lt;br /&gt;
While Obsidian offers a simple Java interface for creating new jobs, Obsidian also provides a way to use annotations to make an arbitrary Java class executable. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;com.carfey.ops.job.SchedulableJob.Schedulable&amp;lt;/code&amp;gt; is a class-level marker annotation indicating that methods are annotated for scheduled execution. Adding this annotation allows you to configure a job in the Obsidian web app or REST API despite the class not implementing &amp;lt;code&amp;gt;SchedulableJob&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;com.carfey.ops.job.SchedulableJob.ScheduledRun&amp;lt;/code&amp;gt; is a method-level annotation to indicate one or more methods to execute at runtime. It has an &amp;lt;code&amp;gt;int executionOrder()&amp;lt;/code&amp;gt; method that defaults to &#039;&#039;0&#039;&#039;. This value indicates the order in which to execute methods. Duplication of execution order is not permitted. Annotated methods must have no arguments and must be public.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Using these annotations precludes you from storing job results or parameterizing your job.&lt;br /&gt;
&lt;br /&gt;
= Interruptable Jobs =&lt;br /&gt;
&lt;br /&gt;
[https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/InterruptableJob.html InterruptableJob Javadoc]&lt;br /&gt;
&lt;br /&gt;
[https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/InterruptableContextJob.html InterruptableContextJob Javadoc]&lt;br /&gt;
&lt;br /&gt;
It is possible to &#039;&#039;&#039;terminate&#039;&#039;&#039; a running job on a best effort basis. [[Job_Forking|Forked Jobs]] can also be interrupted.&lt;br /&gt;
&lt;br /&gt;
In some exceptional cases, it may be necessary or desirable to force termination of a job. Since exposing this functionality for all jobs could result in unexpected and even dangerous results, Obsidian provides two Java interfaces that are used specifically for this function.&lt;br /&gt;
&lt;br /&gt;
The interfaces &amp;lt;code&amp;gt;InterruptableJob&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;InterruptableContextJob&amp;lt;/code&amp;gt; extend &amp;lt;code&amp;gt;SchedulableJob&amp;lt;/code&amp;gt; and flag a job as interruptable. Technically speaking, this means that the main job thread will be interrupted by &amp;lt;code&amp;gt;Thread.interrupt()&amp;lt;/code&amp;gt;, when an interrupt request is received via the UI or REST API.&lt;br /&gt;
&lt;br /&gt;
Both interfaces mandate implementation of a &amp;lt;code&amp;gt;void beforeInterrupt()&amp;lt;/code&amp;gt; method, with the &amp;lt;code&amp;gt;InterruptableContextJob&amp;lt;/code&amp;gt; version supplying the job&#039;s &amp;lt;code&amp;gt;Context&amp;lt;/code&amp;gt; object, which contains the interrupting user through &amp;lt;code&amp;gt;getInterruptUser()&amp;lt;/code&amp;gt;. This method allows for you to perform house-cleaning before Obsidian interrupts the job thread. For example, you may have additional threads to shut down, or other resources to release. You may also want to set a flag on the job instance to indicate to the executing thread that it should shut down, rather than rely on checking &amp;lt;code&amp;gt;Thread.isInterrupted()&amp;lt;/code&amp;gt;. You should attempt to have your &amp;lt;code&amp;gt;beforeInterrupt()&amp;lt;/code&amp;gt; execute in a timely fashion, though it will not interrupt other job scheduling/execution functionality if it takes some time.&lt;br /&gt;
&lt;br /&gt;
It is possible that the job completes either successfully or with failure before the interrupt can proceed. If the interrupt proceeds, the job will be marked as &#039;&#039;&#039;Error&#039;&#039;&#039; and the interruption details will be made available for review in both the [[Admin_Job_Activity|Job Activity]] and [[Admin_Logs|Log]] views.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: After invoking &amp;lt;code&amp;gt;void beforeInterrupt()&amp;lt;/code&amp;gt;, Obsidian will invoke &amp;lt;code&amp;gt;Thread.interrupt()&amp;lt;/code&amp;gt; to try to get the job to abort. Note that &amp;lt;code&amp;gt;Thread.interrupt()&amp;lt;/code&amp;gt; does not forcibly terminate a thread in most cases, and it is up to the job itself to support aborting at an appropriate time when an interrupt is received. This [https://docs.oracle.com/javase/tutorial/essential/concurrency/interrupt.html tutorial] explains the details of thread interrupts.&lt;br /&gt;
&lt;br /&gt;
= Classpath for Building =&lt;br /&gt;
&lt;br /&gt;
To implement jobs in Java, you will need to reference Obsidian base classes in your Java project. &lt;br /&gt;
&lt;br /&gt;
A single library containing everything you need to build Java jobs is found in your installation under the &amp;lt;code&amp;gt;/standalone&amp;lt;/code&amp;gt; directory:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;obsidian.jar&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This library will not conflict with your existing build classpath since it is unique to Obsidian.&lt;br /&gt;
&lt;br /&gt;
To build a custom WAR, you can use the provided WAR artifacts in the Obsidian zip package you downloaded, and customize it in your desired build technology (e.g. Ant, Maven, Gradle, etc.). &lt;br /&gt;
&lt;br /&gt;
For instructions on how to deploy your custom jobs to Obsidian, see [[Getting Started]].&lt;br /&gt;
&lt;br /&gt;
== JVM Forking ==&lt;br /&gt;
[[Job_Forking|Job Forking]] runs each job in its own JVM instance which is started for each execution. This enables hot-swapping of JARs so that jobs can be updated without restarts. By default, this feature works on standalone instances, but other modes can be supported with minor customization.&lt;br /&gt;
&lt;br /&gt;
= Classpath Scanning =&lt;br /&gt;
&lt;br /&gt;
Obsidian supports classpath scanning to find your jobs for display in the [[Admin_Jobs#Adding_.26_Editing_Jobs|job edit screen]]. If you deploy the scheduler separately from the web application, the scheduler node will scan its classpath for available jobs, and the web application will have access to these results.&lt;br /&gt;
&lt;br /&gt;
All classes that implement &amp;lt;code&amp;gt;com.carfey.ops.job.SchedulableJob&amp;lt;/code&amp;gt; or use the &amp;lt;code&amp;gt;com.carfey.ops.job.SchedulableJob.Schedulable&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;com.carfey.ops.job.SchedulableJob.ScheduledRun&amp;lt;/code&amp;gt; annotations will be included, provided they are on the classpath.&lt;br /&gt;
&lt;br /&gt;
To configure classpath scanning, you must specify one or more package prefixes via [[Admin_Scheduler Settings|scheduler settings]]. Select the &amp;quot;Job&amp;quot; category, and locate the &amp;quot;packageScannerPrefix&amp;quot; parameter. Specify your comma delimited list of package prefixes and save your changes. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The prefixes should be as specific as possible to reduce memory overhead. For example, if all your jobs are under &amp;lt;code&amp;gt;com.example.obsidian.jobs&amp;lt;/code&amp;gt;, use the prefix &amp;quot;com.example.obsidian.jobs&amp;quot; rather than &amp;quot;com.example&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:PackageScannerPrefix_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can also configure how often Obsidian will check &amp;quot;packageScannerPrefix&amp;quot; for changes, which results in a re-scan of available jobs. This is done via the &amp;quot;classpathScanFrequency&amp;quot; parameter in the &amp;quot;Job&amp;quot; category in  [[Admin_Scheduler Settings|scheduler settings]].&lt;br /&gt;
&lt;br /&gt;
If you are using Spring and wish to integrate Obsidian and [[Spring_Integration|Spring]], you will likely not need to use this distinct classpath scanning functionality, since jobs found in the Spring context will be available in the [[Admin_Jobs#Adding_.26_Editing_Jobs|job edit screen]] automatically.&lt;br /&gt;
&lt;br /&gt;
= Initializing Jobs on Startup =&lt;br /&gt;
If you&#039;re interested in initializing your jobs into Obsidian on startup without having to write and execute code or manually configure them using the UI, you can use the [[Initializing and Restoring]] functionality.&lt;br /&gt;
&lt;br /&gt;
= Best Practices =&lt;br /&gt;
&lt;br /&gt;
Obsidian&#039;s many features give you multiple ways to solve the same problem, but here are some tips to guide your implementation:&lt;br /&gt;
&lt;br /&gt;
* Use [[#Parameterization|parameters]] to promote reuse in your jobs by making them more generic - this helps avoid builds just for configuration changes. Defined parameters are especially useful to enforce constraints on configuration.&lt;br /&gt;
* Use class inheritance when writing your SchedulableJob classes to share common functionality between different jobs.&lt;br /&gt;
* Use [[#Job Results|job results]] for use in chained jobs. For example, you can chain to a generic archive or FTP transfer job which uses source job results to know what to send.&lt;br /&gt;
* Use [[#Global Parameters|global parameters]] when you are referencing configuration that many jobs require (e.g. database connection info or shared file paths).&lt;br /&gt;
* Avoid catching and not rethrowing exceptions when you want Obsidian to recognize it as a job failure. Obsidian relies on seeing a thrown exception to record job failures.&lt;br /&gt;
* Use [[Scripting_Jobs|script jobs]] to write simple jobs that are used for maintenance or simple tasks, but stick to compiled SchedulableJob classes for critical jobs or performance-sensitive production code.&lt;br /&gt;
* [[Contact_the_Obsidian_Scheduler_Team|Contact us]] if you want suggestions on how to implement your jobs. Our team is happy to help guide you on the right path.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Getting_Started&amp;diff=4147</id>
		<title>Getting Started</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Getting_Started&amp;diff=4147"/>
		<updated>2026-07-31T14:49:52Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This Getting Started is for Obsidian 4.0 and newer versions.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This guide will help you choose a deployment setup that works for you, and then get it running. Before reading this page, you may wish to review Obsidian&#039;s [[Deployment Models]] to understand the different parts of Obsidian or to find out what deployment model works for you.&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve are looking to start writing your own Obsidian jobs, see [[Implementing Jobs]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Supported Platforms =&lt;br /&gt;
&lt;br /&gt;
Please quickly review our supported platforms before continuing.&lt;br /&gt;
&lt;br /&gt;
* Obsidian is OS-independent, and runs on the Java Virtual Machine. It works in a variety of environments, including Linux, OSX and Windows, and can be run inside virtual machines.&lt;br /&gt;
* Obsidian 5.x.x, 6.x.x and 7.x.x run on Java 11 or above (Obsidian 4.x.x runs on Java 1.7 or above). A JRE is sufficient — the embedded Tomcat runtime does not compile JSPs and needs no JDK.&lt;br /&gt;
* Obsidian&#039;s administration web application is a servlet application.&lt;br /&gt;
** 7.x.x has been tested on Tomcat 10.x&lt;br /&gt;
** 6.x.x has been tested on Tomcat 10.x and Jetty 11.x (Jakarta Servlet 5.0)&lt;br /&gt;
** 5.x.x has been tested on Tomcat 9.x and Jetty 10.x (Javax Servlet 4.0)&lt;br /&gt;
** 4.x.x has been tested on Tomcat 7, 8 &amp;amp; 8.5 and Jetty 9.x (Javax Servlet 4.0)&lt;br /&gt;
* It works on all modern browsers and is tested on recent versions of Chrome, Edge and Firefox.&lt;br /&gt;
* Obsidian requires one of the following fully-supported database platforms:&lt;br /&gt;
** MySQL 8.0.x (Obsidian 5.x and higher), 5.5-5.7 (excluding Galera)&lt;br /&gt;
** MariaDB 5.5&lt;br /&gt;
** Oracle 18c, 19c, 21c on Obsidian 5.x and higher&lt;br /&gt;
** Oracle 10g, 11.x, 12c on versions prior to Obsidian 5.0.0&lt;br /&gt;
** PostgreSQL 11-16 on Obsidian 5.x and higher&lt;br /&gt;
** PostgreSQL 9, 10&lt;br /&gt;
** MS SQL Server 2008-2019&lt;br /&gt;
** H2 1.4. &lt;br /&gt;
** &#039;&#039;Larger major versions are likely to work without issues but are not officially supported.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; With some exceptions, clustered scheduler instances require access to contact license servers over the Internet, or access to an internal proxy license server. See [[Licenses &amp;amp; Nodes]] for more information.&lt;br /&gt;
&lt;br /&gt;
== Hardware Requirements ==&lt;br /&gt;
&lt;br /&gt;
We recommend the following minimum hardware for typical installations:&lt;br /&gt;
&lt;br /&gt;
* 1 GHz processor&lt;br /&gt;
* 2 GB RAM&lt;br /&gt;
* 1 GB hard disk space (excluding accumulated logs)&lt;br /&gt;
&lt;br /&gt;
Note that actual requirements may vary depending on job volume, clustering and other factors.&lt;br /&gt;
&lt;br /&gt;
= Choose Your Installation Type =&lt;br /&gt;
&lt;br /&gt;
Obsidian consists of two main processes:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Scheduler&#039;&#039;&#039; - schedules and executes jobs, sends notifications, etc. Also exposes the [[Embedded API]].&lt;br /&gt;
* &#039;&#039;&#039;Admin Web Application&#039;&#039;&#039; - provides management and monitoring UI, plus the [[REST API]] and [[Embedded API]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
These can be run together or separately in the following configurations. You will pick one or more of these options together to provide both the scheduler and admin web application functionality.&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Standalone Scheduler&#039;&#039;&#039; - A Java process that performs job execution. A standalone scheduler installation is generated when you run the Obsidian installer, and may be customized by adding JAR files containing your job code. It is started and stopped by scripts included with the installation. Requires a separate admin web application to be deployed.&lt;br /&gt;
# &#039;&#039;&#039;Embedded Scheduler&#039;&#039;&#039; - Provides job execution like a standalone scheduler, but instead runs as a process embedded within and started from your application. This eliminates the need to customize and deploy a separate artifact which will perform job execution. Requires a separate admin web application to be deployed.&lt;br /&gt;
# &#039;&#039;&#039;Standalone Admin Web Application&#039;&#039;&#039; - A Java servlet application providing Obsidian&#039;s administration capabilities but no scheduling services.  A standalone admin application WAR file is generated when you run the Obsidian installer, and may be customized by adding JAR files containing your job code.  Requires a separate standalone, embedded or combined scheduler and web application to be deployed for job execution to be performed.&lt;br /&gt;
# &#039;&#039;&#039;Combined Scheduler and Admin Web Application&#039;&#039;&#039; - A version of the admin web application which also runs a scheduler service for job execution. A combined scheduler and admin application WAR file is generated when you run the Obsidian installer, and may be customized by adding JAR files containing your job code. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Regardless of the deployment configuration, any time you run more than one scheduler process operating against the same Obsidian database, they will automatically form a cluster and share in job execution.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Choosing which installation type works best for you depends on your specific needs. The setups which are suitable for most cases are, in order: &lt;br /&gt;
&lt;br /&gt;
* Using an &#039;&#039;&#039;Embedded Scheduler&#039;&#039;&#039; (option 2), along with a &#039;&#039;&#039;Standalone Admin Web Application&#039;&#039;&#039; (option 3). This approach lets you embed Obsidian into your existing application without having to alter the Obsidian artifacts to include your job code. Instead, Obsidian&#039;s required libraries are included in your application and they are deployed together.&lt;br /&gt;
* Using a  &#039;&#039;&#039;Combined Scheduler and Admin Web Application&#039;&#039;&#039; (option 4) alone or in a cluster. This is suitable if you don&#039;t have another application containing job code for Obsidian to execute, or if you simply want to use Obsidian&#039;s scripting support, which doesn&#039;t require deploying JAR files.&lt;br /&gt;
&lt;br /&gt;
= Downloading Obsidian =&lt;br /&gt;
Just go to our [https://web.obsidianscheduler.com/download/ download page], and grab the latest Obsidian&#039;s installation zip file. There is no need to register or obtain a license before downloading.&lt;br /&gt;
&lt;br /&gt;
Once you&#039;ve downloaded the zip file, extract it to a directory of your choice.&lt;br /&gt;
&lt;br /&gt;
If you are trying to upgrade to a newer version of Obsidian, see [[Upgrading_Obsidian|upgrade]] instructions.&lt;br /&gt;
&lt;br /&gt;
= Quick Start for Evaluation Purposes =&lt;br /&gt;
&lt;br /&gt;
If you wish to quickly try Obsidian without minimal setup required, follow these steps. Otherwise, skip ahead to [[#Initial Setup|Initial Setup]].&lt;br /&gt;
&lt;br /&gt;
== Really Quick Method Using Embedded Database ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;We do not recommend this setup for production use. &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Ensure you have a JDK (11 or up for Obsidian 6.x.x &amp;amp; 5.x.x, 1.7 or up for Obsidian 4.x.x) installed and that the &amp;lt;code&amp;gt;JAVA_HOME&amp;lt;/code&amp;gt; environment variable is set to your JDK installation directory.&lt;br /&gt;
# Unzip the Obsidian download zip file to a directory of your choice.&lt;br /&gt;
# At the command line, in the zip extraction directory, run the [[Installation_Guide|installer]] using this command, substituting the appropriate Obsidian version number: &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar h2-tomcat-quick-start.xml&amp;lt;/code&amp;gt;&lt;br /&gt;
# Start Obsidian using the command: &amp;lt;code&amp;gt;./webObsidian.sh start scheduler&amp;lt;/code&amp;gt; for Linux or &amp;lt;code&amp;gt;webObsidian.bat start scheduler&amp;lt;/code&amp;gt; for Windows.&lt;br /&gt;
# Go to http://localhost:8080 in your browser and check out your fully functional Obsidian web application and scheduler! You can log in to the admin web application with the default user &#039;&#039;admin&#039;&#039; and password &#039;&#039;changeme&#039;&#039;.&lt;br /&gt;
# When you&#039;re done, stop Obsidian using the command: &amp;lt;code&amp;gt;./webObsidian.sh stop&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;webObsidian.bat stop&amp;lt;/code&amp;gt; for Windows.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; If you restart the Quick Start installation and see &amp;quot;lock wait timeout&amp;quot; or similar errors in the log screen, you may have to delete your embedded H2 database lock file. By default, the file name is &#039;&#039;obsidian.lock.db&#039;&#039; and is located in the user home directory.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; If you see a &amp;lt;code&amp;gt;java.net.BindException: Permission denied&amp;lt;/code&amp;gt; error on startup, you will need to use an alternate port. See [[Troubleshooting]] for details on how to change this.&lt;br /&gt;
&lt;br /&gt;
== Quick Method Using Existing Database ==&lt;br /&gt;
&lt;br /&gt;
If you wish to get Obsidian running quickly using a database platform you already have running, follow these steps:&lt;br /&gt;
&lt;br /&gt;
# Ensure you have a JDK (11 or up for Obsidian 6.x.x and 5.x.x, 1.7 or up for Obsidian 4.x.x) installed and that the &amp;lt;code&amp;gt;JAVA_HOME&amp;lt;/code&amp;gt; environment variable is set to your JDK installation directory.&lt;br /&gt;
# Unzip the Obsidian download zip file to a directory of your choice.&lt;br /&gt;
# At the command line, in the zip extraction directory, run the [[Installation_Guide|installer]] using this command, substituting the appropriate Obsidian version number: &amp;lt;code&amp;gt;java -jar Obsidian-Install-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;-&#039;&#039;n&#039;&#039;.jar&amp;lt;/code&amp;gt;&lt;br /&gt;
# Start Obsidian using the command: &amp;lt;code&amp;gt;./webObsidian.sh start scheduler&amp;lt;/code&amp;gt; for Linux or &amp;lt;code&amp;gt;webObsidian.bat start scheduler&amp;lt;/code&amp;gt; for Windows.&lt;br /&gt;
# Go to http://localhost:8080 in your browser and check out your fully functional Obsidian web application and scheduler! You can log in to the admin web application with the default user &#039;&#039;admin&#039;&#039; and password &#039;&#039;changeme&#039;&#039;.&lt;br /&gt;
# When you&#039;re done, stop Obsidian using the command: &amp;lt;code&amp;gt;./webObsidian.sh stop&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;webObsidian.bat stop&amp;lt;/code&amp;gt; for Windows.&lt;br /&gt;
&lt;br /&gt;
= Initial Setup  =&lt;br /&gt;
&lt;br /&gt;
This section covers the setup required after you&#039;ve selected your [[#Choose_Your_Installation_Type|installation type]]. &lt;br /&gt;
&lt;br /&gt;
== Database ==&lt;br /&gt;
&lt;br /&gt;
Obsidian requires a database which must be created before running Obsidian. Multiple [[#Supported Platforms|database platforms]] are supported. Obsidian can share a database/schema with another application.&lt;br /&gt;
&lt;br /&gt;
Note that the database must exist before deployment, but by default Obsidian will automatically create all required tables.&lt;br /&gt;
&lt;br /&gt;
Later, you will specify database connection parameters within Obsidian&#039;s installer to tell it how to connect. If you need to configure advanced settings or change settings directly after you&#039;ve run run the installer, see [[Advanced Configuration]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Obsidian needs to create the tables in the target database. If the schema is shared with your application’s tables, please ensure there are no name conflicts. If there are conflicts, separate schemas/databases can be used, or a table prefix can be specified as shown in [[Advanced_Configuration#Database_Properties|Advanced Configuration]]. [[Obsidian Tables]] lists the tables Obsidian will created upon first deployment.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Manual Schema Creation ===&lt;br /&gt;
&lt;br /&gt;
When you start Obsidian, it will automatically detect and create missing tables in your target database. However, if you wish to run Obsidian with a database user that does not have create privileges, or simply wish to prepare your database structure ahead of time, you can use the database scripts provided with your download zip file under the &amp;quot;db_scripts&amp;quot; directory.&lt;br /&gt;
&lt;br /&gt;
First create your database, and locate the &amp;quot;complete.sql&amp;quot; file under the directory for the database you are using under the &amp;quot;db_scripts&amp;quot; directory. Statements within this file contain the token &amp;quot;$px$&amp;quot; to enable table prefixes. Before you run the SQL file, replace all instances of the token &amp;quot;$px$&amp;quot; (without quotes) with a table prefix 6 characters or shorter (letters and underscores supported), or with blank.&lt;br /&gt;
&lt;br /&gt;
Then, in your SQL client of choice, execute the script and all the required Obsidian tables will be created.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Alternate Oracle Schemas ===&lt;br /&gt;
&lt;br /&gt;
After allowing Obsidian to create all the required tables and initial data by deploying it a single time, Obsidian can be run with an alternate Oracle user by specifying the target schema on Oracle databases. See [[Advanced_Configuration#Database_Properties|Advanced Configuration]] for the property to set.&lt;br /&gt;
&lt;br /&gt;
If you use a separate user from the schema owner, you must grant the user [[Obsidian_Tables#Oracle Privileges|certain privileges]] for Obsidian to function correctly.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== MS SQL Server Snapshot Isolation ===&lt;br /&gt;
&lt;br /&gt;
For maximum compatibility and to avoid deadlocks, MS SQL Server should be configured to use [https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql/snapshot-isolation-in-sql-server read committed snapshot isolation].&lt;br /&gt;
&lt;br /&gt;
This can be enabled on your database by running the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ALTER DATABASE MyDatabase SET ALLOW_SNAPSHOT_ISOLATION ON&lt;br /&gt;
ALTER DATABASE MyDatabase SET READ_COMMITTED_SNAPSHOT ON&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Authentication ==&lt;br /&gt;
&lt;br /&gt;
By default, Obsidian manages its own set users and logins to restrict access to the admin web application and REST API. A default “admin” user is created when the scheduler is first deployed and no additional setup is required.&lt;br /&gt;
&lt;br /&gt;
Alternatively, to use LDAP authentication, select the LDAP option in the installer and enter your LDAP details.&lt;br /&gt;
&lt;br /&gt;
As of 7.0.0, you can also select OAuth / OIDC authentication to enable Single Sign-On via an external identity provider. See [[Authenticator#OAuthAuthenticator_(OIDC_SSO)|OAuthAuthenticator]] for details.&lt;br /&gt;
&lt;br /&gt;
Finally, you may implement your own [[authenticator|custom authenticator]] Java class, which you also enter when running the installer.&lt;br /&gt;
&lt;br /&gt;
= Running the Installer =&lt;br /&gt;
&lt;br /&gt;
Now that you&#039;ve chosen your installation type and performed initial setup of your database and authentication, you can run Obsidian&#039;s installer to configure and build a set of artifacts that you can either use to embed Obsidian, or deploy directly.&lt;br /&gt;
&lt;br /&gt;
To run the installer, please follow our detailed [[Installation_Guide|installation guide]].&lt;br /&gt;
&lt;br /&gt;
=== Installer-Created Artifacts ===&lt;br /&gt;
&lt;br /&gt;
Once you&#039;ve run the installer, your installation directory will look something like the following, depending on which artifacts you chose to generate.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
├───db_scripts&lt;br /&gt;
└───Obsidian-7.0.0&lt;br /&gt;
    │   com.carfey.yaml.reference&lt;br /&gt;
    │   obsidian.war&lt;br /&gt;
    │   obsidian-builtin-job-src.jar&lt;br /&gt;
    │   obsidianForkedJob.bat&lt;br /&gt;
    │   obsidianForkedJob.sh&lt;br /&gt;
    │   obsidian-javadoc.zip&lt;br /&gt;
    │   standaloneObsidian.bat&lt;br /&gt;
    │   standaloneObsidian.sh&lt;br /&gt;
    │   standaloneObsidianAdmin.war&lt;br /&gt;
    │   webObsidian.bat&lt;br /&gt;
    │   webObsidian.sh&lt;br /&gt;
    │&lt;br /&gt;
    ├───embed-tomcat-lib  (present when an Embedded Tomcat pack is selected)&lt;br /&gt;
    ├───license&lt;br /&gt;
    ├───linux             (present on Unix when an Embedded Tomcat pack is selected)&lt;br /&gt;
    ├───logs&lt;br /&gt;
    └───standalone&lt;br /&gt;
        (various JAR files...)&lt;br /&gt;
        obsidian-yaml-configuration.jar&lt;br /&gt;
        obsidian.jar&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The installer creates a top-level directory named &#039;&#039;&#039;Obsidian-n.n.n&#039;&#039;&#039; containing generated artifacts. The key files and directories are:&lt;br /&gt;
&lt;br /&gt;
; com.carfey.yaml.reference or com.carfey.properties.reference&lt;br /&gt;
: Copy of the configuration file generated by the installer.&lt;br /&gt;
; obsidian.war&lt;br /&gt;
: Combined scheduler and admin web application WAR. Deploy directly to a servlet container, or run locally using the Embedded Tomcat pack (see below). Customise by adding your job code and dependencies as JARs to &#039;&#039;&#039;/WEB-INF/lib&#039;&#039;&#039; within the archive.&lt;br /&gt;
; standaloneObsidian.bat and standaloneObsidian.sh&lt;br /&gt;
: Windows and Linux scripts to start the standalone scheduler process with no web application. Customise by adding your job code and dependencies as JARs to the &#039;&#039;&#039;standalone&#039;&#039;&#039; directory.&lt;br /&gt;
; webObsidian.bat and webObsidian.sh&lt;br /&gt;
: Windows and Linux scripts to start Obsidian using the embedded Tomcat runtime (requires the &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; or &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; pack). Use &#039;&#039;&#039;start scheduler&#039;&#039;&#039; to run the combined scheduler+admin WAR, or &#039;&#039;&#039;start adminOnly&#039;&#039;&#039; to run the standalone admin WAR. Stop with &#039;&#039;&#039;stop&#039;&#039;&#039;.&lt;br /&gt;
; standaloneObsidianAdmin.war&lt;br /&gt;
: Standalone admin web application WAR (no scheduler). Deploy directly to a servlet container or run via the embedded Tomcat pack.&lt;br /&gt;
; embed-tomcat-lib&lt;br /&gt;
: Present when an Embedded Tomcat pack is selected. Contains the embedded Tomcat runtime JARs used by the &#039;&#039;&#039;webObsidian&#039;&#039;&#039; scripts.&lt;br /&gt;
; linux&lt;br /&gt;
: Present on Unix when an Embedded Tomcat pack is selected. Optional user-level systemd unit and desktop-entry templates, plus &#039;&#039;&#039;install-embedded-linux-integration.sh&#039;&#039;&#039; and &#039;&#039;&#039;README-linux-integration.md&#039;&#039;&#039;. See [[#Running_Obsidian_as_a_Linux_user_service|Running Obsidian as a Linux user service]].&lt;br /&gt;
; standalone&lt;br /&gt;
: Directory containing JARs required to run the standalone scheduler process. &#039;&#039;&#039;obsidian-yaml-configuration.jar&#039;&#039;&#039; or &#039;&#039;&#039;obsidian-properties-configuration.jar&#039;&#039;&#039; contains the Obsidian configuration file. To make configuration changes for the standalone scheduler, update the configuration file within this JAR.&lt;br /&gt;
&lt;br /&gt;
= Starting Obsidian with embedded Tomcat =&lt;br /&gt;
&lt;br /&gt;
If you selected the &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; or &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; pack during installation, use the &#039;&#039;&#039;webObsidian&#039;&#039;&#039; scripts to start and stop Obsidian.&lt;br /&gt;
&lt;br /&gt;
From the Obsidian install directory:&lt;br /&gt;
&lt;br /&gt;
 webObsidian.bat start scheduler&lt;br /&gt;
 (Unix: ./webObsidian.sh start scheduler)&lt;br /&gt;
&lt;br /&gt;
For standalone admin only:&lt;br /&gt;
&lt;br /&gt;
 webObsidian.bat start adminOnly&lt;br /&gt;
&lt;br /&gt;
Stop:&lt;br /&gt;
&lt;br /&gt;
 webObsidian.bat stop&lt;br /&gt;
 (Unix: ./webObsidian.sh stop)&lt;br /&gt;
&lt;br /&gt;
Default HTTP port is &#039;&#039;&#039;8080&#039;&#039;&#039;. To change ports, pass &#039;&#039;&#039;-Dembedded.tomcat.http.port&#039;&#039;&#039;, &#039;&#039;&#039;-Dembedded.tomcat.stop.port&#039;&#039;&#039;, and &#039;&#039;&#039;-Dembedded.tomcat.stop.key&#039;&#039;&#039; as JVM arguments before starting.&lt;br /&gt;
&lt;br /&gt;
On Linux you can also run Obsidian as a user-level systemd service, or launch it from a desktop entry, instead of calling the script by hand — see [[#Running_Obsidian_as_a_Linux_user_service|Running Obsidian as a Linux user service]] below.&lt;br /&gt;
&lt;br /&gt;
If you are deploying &#039;&#039;&#039;obsidian.war&#039;&#039;&#039; to your own external servlet container instead, refer to your container&#039;s deployment documentation.&lt;br /&gt;
&lt;br /&gt;
= Running Obsidian as a Linux user service =&lt;br /&gt;
&lt;br /&gt;
On Linux, an embedded Tomcat pack can install a user-level systemd unit and a desktop entry — see [[Installation_Guide#Linux_desktop_and_systemd_integration|Linux desktop and systemd integration]] for the installer option and the manual &#039;&#039;&#039;linux/install-embedded-linux-integration.sh&#039;&#039;&#039; script. Installing those files does not enable or start anything, so enable the service yourself:&lt;br /&gt;
&lt;br /&gt;
 systemctl --user enable --now obsidian-embedded@scheduler.service&lt;br /&gt;
&lt;br /&gt;
Check and stop it with:&lt;br /&gt;
&lt;br /&gt;
 systemctl --user status obsidian-embedded@scheduler.service&lt;br /&gt;
 systemctl --user stop obsidian-embedded@scheduler.service&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;./webObsidian.sh stop&#039;&#039; also stops a service-started instance. Do &#039;&#039;&#039;not&#039;&#039;&#039; run &#039;&#039;./webObsidian.sh start scheduler&#039;&#039; by hand while the service is running — both bind the same HTTP and stop ports.&lt;br /&gt;
&lt;br /&gt;
== Ports ==&lt;br /&gt;
&lt;br /&gt;
The unit and the desktop entry both invoke &#039;&#039;&#039;webObsidian.sh&#039;&#039;&#039;, so they use the script&#039;s ports: HTTP &#039;&#039;&#039;8080&#039;&#039;&#039; and stop port &#039;&#039;&#039;9597&#039;&#039;&#039;. Passing &#039;&#039;-Dembedded.tomcat.*&#039;&#039; on a &#039;&#039;java&#039;&#039; command line has no effect on the service — edit the values in &#039;&#039;&#039;webObsidian.sh&#039;&#039;&#039; before enabling it.&lt;br /&gt;
&lt;br /&gt;
== Running without an active login session ==&lt;br /&gt;
&lt;br /&gt;
This is a &#039;&#039;&#039;user&#039;&#039;&#039; service, so by default it runs only while that user has a login session and stops at logout. To keep Obsidian running after logout and start it at boot, enable lingering for the account:&lt;br /&gt;
&lt;br /&gt;
 loginctl enable-linger &amp;lt;nowiki&amp;gt;&amp;lt;username&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you want Obsidian to run as a system service independent of any user account, write your own unit under &#039;&#039;/etc/systemd/system/&#039;&#039; using &#039;&#039;&#039;linux/obsidian-embedded@.service.in&#039;&#039;&#039; as a starting point.&lt;br /&gt;
&lt;br /&gt;
== Standalone admin installs ==&lt;br /&gt;
&lt;br /&gt;
The generated unit runs &#039;&#039;&#039;webObsidian.sh start scheduler&#039;&#039;&#039;. If you installed only the &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; pack, change &#039;&#039;ExecStart&#039;&#039; in &#039;&#039;~/.config/systemd/user/obsidian-embedded@scheduler.service&#039;&#039; to use &#039;&#039;&#039;start adminOnly&#039;&#039;&#039; before enabling the service.&lt;br /&gt;
&lt;br /&gt;
== Removing the integration ==&lt;br /&gt;
&lt;br /&gt;
 systemctl --user disable --now obsidian-embedded@scheduler.service&lt;br /&gt;
 rm ~/.config/systemd/user/obsidian-embedded@scheduler.service&lt;br /&gt;
 rm ~/.local/share/applications/obsidian-embedded.desktop&lt;br /&gt;
 systemctl --user daemon-reload&lt;br /&gt;
&lt;br /&gt;
The same details ship as &#039;&#039;&#039;linux/README-linux-integration.md&#039;&#039;&#039; in your install directory.&lt;br /&gt;
&lt;br /&gt;
= Embedding Obsidian &amp;amp; Customizing Artifacts =&lt;br /&gt;
&lt;br /&gt;
The Obsidian scheduler process needs Java classpath access to your jobs so it can detect valid jobs and execute them. This requires a bit of customization before you are ready to run custom Java jobs. If you still haven&#039;t created your custom jobs, consult [[Implementing Jobs]] before proceeding. If you only intend to use [[Scripting Jobs]], customization will not be required and you can skip to [[#Deployment|Deployment]].&lt;br /&gt;
&lt;br /&gt;
If you want to embed Obsidian into your application or need to deploy jobs and their dependent libraries to the generated Obsidian artifacts, follow the steps in the appropriate sections below.&lt;br /&gt;
&lt;br /&gt;
Note that only Obsidian scheduler processes need to be customized to include your job code. The admin web application itself does not require classpath access to your jobs.&lt;br /&gt;
&lt;br /&gt;
== Embedding Obsidian - Importing Libraries and Properties File ==&lt;br /&gt;
&lt;br /&gt;
To embed Obsidian, you will need to update your application build to bring in the JAR files it requires, along with the Obsidian properties file. Simply follow these steps:&lt;br /&gt;
&lt;br /&gt;
# Import all required [[Advanced_Configuration#Dependent_Libraries|Obsidian JAR files]] into your project and reference them in your build files. &lt;br /&gt;
#* These can be obtained from the &amp;lt;code&amp;gt;standalone&amp;lt;/code&amp;gt; directory of your installation. Make sure you &#039;&#039;&#039;exclude&#039;&#039;&#039; &amp;lt;code&amp;gt;obsidian-(yaml/properties)-configuration.jar&amp;lt;/code&amp;gt;&lt;br /&gt;
#* Ensure your Maven, Gradle or Ant files are updated to include all required libraries in your build, along with your IDE.&lt;br /&gt;
# Make a copy of &amp;lt;code&amp;gt;com.carfey.yaml.reference&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;com.carfey.properties.reference&amp;lt;/code&amp;gt; from your installation with the &amp;lt;code&amp;gt;.reference&amp;lt;/code&amp;gt; suffix removed, and add it to your project&#039;s classpath.&lt;br /&gt;
#* This is typically just a matter of adding it to your project&#039;s resources directory.&lt;br /&gt;
#* From now on, you can update this file to change the various configuration options such as database connection details.&lt;br /&gt;
#* You may wish to leave this file out of source control and let developers configure their own installations. However, any running Obsidian instance will need access to a valid [[Advanced_Configuration#Properties.2FYaml_File|properties file]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Maven users:&#039;&#039;&#039; Note that we do not publish Maven artifacts for Obsidian, so you will not be able to include them by referencing a public repository.&lt;br /&gt;
&lt;br /&gt;
== Adding Custom Jobs to Standalone Scheduler ==&lt;br /&gt;
&lt;br /&gt;
In order for the standalone scheduler to detect and execute custom jobs written in Java, you will need to ensure Obsidian&#039;s classpath contains the compiled job code, along with its dependencies.&lt;br /&gt;
&lt;br /&gt;
To do so, you need to perform just one step before starting the standalone scheduler:&lt;br /&gt;
&lt;br /&gt;
# Copy JAR files containing custom Obsidian jobs and all runtime dependencies to the &amp;lt;code&amp;gt;standalone&amp;lt;/code&amp;gt; directory in your installation.&lt;br /&gt;
#* These JARs should be alongside &amp;lt;code&amp;gt;obsidian.jar&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* All JAR files in this directory will be automatically added to the classpath when run via the &amp;lt;code&amp;gt;standaloneObsidian.sh&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;standaloneObsidian.bat&amp;lt;/code&amp;gt; scripts.&lt;br /&gt;
&lt;br /&gt;
== Adding Custom Jobs to Combined Scheduler and Admin Web Application ==&lt;br /&gt;
&lt;br /&gt;
In order for the combined scheduler and admin web application to detect and execute custom jobs written in Java, you will need to ensure Obsidian&#039;s classpath contains the compiled job code, along with its dependencies.&lt;br /&gt;
&lt;br /&gt;
To do so, you need to perform just one step before deploying the Obsidian WAR file:&lt;br /&gt;
&lt;br /&gt;
# Copy JAR files containing custom Obsidian jobs and all runtime dependencies to the &amp;lt;code&amp;gt;WEB-INF/lib&amp;lt;/code&amp;gt; directory within the WAR archive.&lt;br /&gt;
#* These JARs should be alongside &amp;lt;code&amp;gt;obsidian.jar&amp;lt;/code&amp;gt;.&lt;br /&gt;
#* WAR files use the ZIP format and can be extracted and recompressed with normal ZIP tools or the JDK &amp;lt;code&amp;gt;jar&amp;lt;/code&amp;gt; utility.&lt;br /&gt;
&lt;br /&gt;
== Updating the Properties File in Admin Web Applications ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note that updating the WAR artifact is not necessary to perform properties file changes when using an external properties file as described [[Advanced_Configuration#Properties_File|here]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you need to change [[Advanced_Configuration#Properties_File|configuration properties]] for a standalone admin web application or combined scheduler and admin web application, follow these steps:&lt;br /&gt;
&lt;br /&gt;
# Obtain the WAR artifact from your installation that requires changes. This is either &amp;lt;code&amp;gt;obsidian.war&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;standaloneObsidianAdmin.war&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Extract the WAR file to a clean directory.&lt;br /&gt;
# Edit the &amp;lt;code&amp;gt;com.carfey.(yaml/properties)&amp;lt;/code&amp;gt; file within the subdirectory &amp;lt;code&amp;gt;WEB-INF/classes&amp;lt;/code&amp;gt; and save your changes.&lt;br /&gt;
# Rebundled the WAR artifact using a zip utility or the JDK &amp;lt;code&amp;gt;jar&amp;lt;/code&amp;gt; utility.&lt;br /&gt;
#* Some zip utilities such as 7Zip will allow you to edit files within an archive directly without the previous steps. It is generally safe to do so.&lt;br /&gt;
&lt;br /&gt;
= Deployment =&lt;br /&gt;
&lt;br /&gt;
You are now ready to deploy your scheduler and/or admin web application.&lt;br /&gt;
&lt;br /&gt;
== Embedded Scheduler ==&lt;br /&gt;
&lt;br /&gt;
When Obsidian is embedded, there is no separate deployment process. However, you will need to start and stop Obsidian with your application. If you are using Spring, we recommend you use our [[Spring Integration]] instead, which will automatically start and stop the scheduler for you.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
During your application startup, once it is fully initialized (or as close as possible), start Obsidian, and save a reference to the returned result so you can shut it down later:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// This will start the scheduler on the first call to get().&lt;br /&gt;
&lt;br /&gt;
com.carfey.ops.job.SchedulerStarter starter = com.carfey.ops.job.SchedulerStarter.get(com.carfey.ops.job.SchedulerStarter.SchedulerMode.EMBEDDED);&lt;br /&gt;
&lt;br /&gt;
// ...&lt;br /&gt;
&lt;br /&gt;
// Later, we need to gracefully shut down the scheduler &lt;br /&gt;
starter.shutDown();&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will run Obsidian&#039;s scheduler process, but will not include any of the web application or the REST API. This functionality is provided by the admin web application.&lt;br /&gt;
&lt;br /&gt;
== Standalone Admin Web Application ==&lt;br /&gt;
&lt;br /&gt;
To deploy the standalone admin web application, simply deploy the &amp;lt;code&amp;gt;standaloneObsidianAdmin.war&amp;lt;/code&amp;gt; file your servlet container of choice (e.g. Tomcat) after configuring it appropriately. You may rename the WAR file to have it deployed under a different context path, if desired (e.g. &amp;quot;ROOT.war&amp;quot; or &amp;quot;obsidian.war&amp;quot;). Consult your servlet documentation to find out how to deploy the application and start the servlet container.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; If you selected the &#039;&#039;&#039;Obsidian Standalone Admin Embedded Tomcat JAR&#039;&#039;&#039; pack, you can use the &#039;&#039;&#039;webObsidian&#039;&#039;&#039; scripts included in your installation instead of deploying the WAR. This will make the application accessible at http://localhost:8080.&lt;br /&gt;
&lt;br /&gt;
* To start &lt;br /&gt;
** Linux: &amp;lt;code&amp;gt;./webObsidian.sh start adminOnly&amp;lt;/code&amp;gt;&lt;br /&gt;
** Windows: &amp;lt;code&amp;gt;webObsidian.bat start adminOnly&amp;lt;/code&amp;gt;&lt;br /&gt;
* To stop&lt;br /&gt;
** Linux: &amp;lt;code&amp;gt;./webObsidian.sh stop&amp;lt;/code&amp;gt; &lt;br /&gt;
** Windows: &amp;lt;code&amp;gt;webObsidian.bat stop&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Security Note:&#039;&#039;&#039; By default, the admin web application allows non-secure connections. If you wish to force secure connections through HTTPS, you can edit the web.xml in your war and uncomment the &amp;lt;security-constraint&amp;gt; element in the file. This will force all requests to redirect to an encrypted connection. For details on setting up SSL on your servlet container, refer to its documentation.&lt;br /&gt;
&lt;br /&gt;
== Standalone Scheduler ==&lt;br /&gt;
&lt;br /&gt;
To deploy and run the standalone scheduler, you may use the provided scripts in your installation directory:&lt;br /&gt;
* Linux: &amp;lt;code&amp;gt;./standaloneObsidian.sh start&amp;lt;/code&amp;gt;&lt;br /&gt;
* Windows: &amp;lt;code&amp;gt;standaloneObsidian.bat start&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You should always stop Obsidian gracefully when possible by using:&lt;br /&gt;
* Linux: &amp;lt;code&amp;gt;./standaloneObsidian.sh stop&amp;lt;/code&amp;gt;&lt;br /&gt;
* Windows: &amp;lt;code&amp;gt;standaloneObsidian.bat stop&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Both the start and stop commands may be supplied an additional argument to override the &amp;lt;code&amp;gt;listenerPort&amp;lt;/code&amp;gt; which defaults to 10451.&lt;br /&gt;
&lt;br /&gt;
Rather than use one of the provided scripts, you can invoke the equivalent Java command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
java com.carfey.ops.job.SchedulerStarter start &amp;lt;listenerPort&amp;gt;&lt;br /&gt;
java com.carfey.ops.job.SchedulerStarter stop &amp;lt;listenerPort&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Combined Scheduler and Admin Web Application ==&lt;br /&gt;
&lt;br /&gt;
To deploy the combined scheduler and admin web application, simply deploy the &amp;lt;code&amp;gt;obsidian.war&amp;lt;/code&amp;gt; file your servlet container of choice (e.g. Tomcat) after configuring it appropriately. You may rename the WAR file to have it deployed under a different context path, if desired (e.g. &amp;quot;ROOT.war&amp;quot;). Consult your servlet documentation to find out how to deploy the application and start the servlet container.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; If you selected the &#039;&#039;&#039;Obsidian Embedded Tomcat JAR&#039;&#039;&#039; pack, you can use the &#039;&#039;&#039;webObsidian&#039;&#039;&#039; scripts included in your installation instead of deploying the WAR. This will make the web application accessible at the URL http://localhost:8080.&lt;br /&gt;
&lt;br /&gt;
* To start &lt;br /&gt;
** Linux: &amp;lt;code&amp;gt;./webObsidian.sh start scheduler&amp;lt;/code&amp;gt;&lt;br /&gt;
** Windows: &amp;lt;code&amp;gt;webObsidian.bat start scheduler&amp;lt;/code&amp;gt;&lt;br /&gt;
* To stop&lt;br /&gt;
** Linux: &amp;lt;code&amp;gt;./webObsidian.sh stop&amp;lt;/code&amp;gt; &lt;br /&gt;
** Windows: &amp;lt;code&amp;gt;webObsidian.bat stop&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Security Note:&#039;&#039;&#039; By default, the admin web application allows non-secure connections. If you wish to force secure connections through HTTPS, you can edit the web.xml in your war and uncomment the &amp;lt;security-constraint&amp;gt; element in the file. This will force all requests to redirect to an encrypted connection. For details on setting up SSL on your servlet container, refer to its documentation.&lt;br /&gt;
&lt;br /&gt;
== Embedding the Obsidian Web Application in Another Web Application ==&lt;br /&gt;
&lt;br /&gt;
We do not generally recommend attempting to merge the Obsidian admin web application with another servlet web application, but it can be done by following these steps:&lt;br /&gt;
&lt;br /&gt;
To embed the full Obsidian web application in an existing web application, you will need to extract the necessary sections from the &amp;lt;code&amp;gt;WEB-INF/web.xml&amp;lt;/code&amp;gt; file in the &amp;lt;code&amp;gt;obsidian.war&amp;lt;/code&amp;gt; file and merge them into your application&#039;s &amp;lt;code&amp;gt;web.xml&amp;lt;/code&amp;gt; file. This includes all &amp;quot;listener&amp;quot;, &amp;quot;servlet&amp;quot;, &amp;quot;servlet-mapping&amp;quot;, &amp;quot;jsp-config&amp;quot;, &amp;quot;filter&amp;quot;, &amp;quot;filter-mapping&amp;quot; elements, and optionally &amp;quot;welcome-file-list&amp;quot; and &amp;quot;error-page&amp;quot; elements. Ensure that all servlet paths are maintained. If you are using a different servlet specification version, you may need to update the mappings to the appropriate format.&lt;br /&gt;
&lt;br /&gt;
Note the use of our &amp;lt;code&amp;gt;com.carfey.ops.servlet.StartupShutdownListener&amp;lt;/code&amp;gt; which takes care of starting up the scheduler instance and using the web container&#039;s default shutdown mechanism to ensure graceful shutdown. This listener will start Obsidian&#039;s scheduler process and initialize the Obsidian web application. &lt;br /&gt;
&lt;br /&gt;
If you wish to embed Obsidian&#039;s web application without the scheduler running, include the following parameter element in your &amp;lt;code&amp;gt;web.xml&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;context-param&amp;gt;&lt;br /&gt;
   &amp;lt;param-name&amp;gt;schedulerEnabled&amp;lt;/param-name&amp;gt;&lt;br /&gt;
   &amp;lt;param-value&amp;gt;false&amp;lt;/param-value&amp;gt;&lt;br /&gt;
&amp;lt;/context-param&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Additional Deployment Details ==&lt;br /&gt;
&lt;br /&gt;
=== Classpath Notes ===&lt;br /&gt;
&lt;br /&gt;
To run a scheduler with your custom code and jobs, you need to ensure the scheduler process classpath includes your code packaged into JAR files, as described [[#Embedding_Obsidian_.26_Customizing_Artifacts|above]]. &lt;br /&gt;
&lt;br /&gt;
If a compiled job is updated, you will have to restart your application or the servlet container after deploying updated jars, unless you are using [[Job Forking]].&lt;br /&gt;
&lt;br /&gt;
=== Setting Host Names ===&lt;br /&gt;
Obsidian instances will automatically assign themselves host names if no host name is explicitly set, but you may wish to give them explicit names to make scheduling and monitoring simpler.&lt;br /&gt;
&lt;br /&gt;
If you wish to assign explicit names, simply set the Java system property &amp;lt;code&amp;gt;schedulerDesignation&amp;lt;/code&amp;gt; to the host name of your choice. For example, if starting an instance using the standalone scheduler using &amp;lt;code&amp;gt;java&amp;lt;/code&amp;gt; directly, simply add the value &amp;lt;code&amp;gt;-DschedulerDesignation=myHostName&amp;lt;/code&amp;gt; to the end of the command.&lt;br /&gt;
&lt;br /&gt;
You may also use a properties file setting for the host name as detailed in [[Advanced Configuration]].&lt;br /&gt;
&lt;br /&gt;
=== Disabling Automatic Database Updates ===&lt;br /&gt;
&lt;br /&gt;
Obsidian automatically applies schema updates and data upgrades to its database on startup. In some cases, it may be desirable to disable this once the database has been fully initialized and upgrades to new versions are not going to be deployed. For example, you may wish to run Obsidian with a user who does not have privileges to modify the database schema.&lt;br /&gt;
&lt;br /&gt;
To do so, you can either set a Java system property, or if you are using the admin web application WAR, you can add a setting to your &amp;lt;code&amp;gt;WEB-INF/web.xml&amp;lt;/code&amp;gt; within the archive.&lt;br /&gt;
&lt;br /&gt;
As a system property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-DstartupRunnerClass=com.carfey.ops.run.NullRunner&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;WEB-INF/web.xml&amp;lt;/code&amp;gt;, after any &amp;lt;code&amp;gt;listener&amp;lt;/code&amp;gt; elements:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;context-param&amp;gt;&lt;br /&gt;
    &amp;lt;param-name&amp;gt;startupRunnerClass&amp;lt;/param-name&amp;gt;&lt;br /&gt;
    &amp;lt;param-value&amp;gt;com.carfey.ops.run.NullRunner&amp;lt;/param-value&amp;gt;&lt;br /&gt;
&amp;lt;/context-param&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Disabling DDL Updates ===&lt;br /&gt;
&lt;br /&gt;
It may also be helpful to apply schema updates (DDL) outside Obsidian through a privileged account, but allow Obsidian upgrades to make the necessary data initialization/modifications. This can be done using a &#039;&#039;&#039;skipDDL&#039;&#039;&#039; configuration property documented under [[Advanced_Configuration#Miscellaneous_Properties | Advanced Configuration]].&lt;br /&gt;
&lt;br /&gt;
=== Disabling Job Scheduling in the Web Application ===&lt;br /&gt;
&lt;br /&gt;
If you have combined scheduler and admin web application WAR already built, you can easily tweak it to disable the scheduler process. &lt;br /&gt;
&lt;br /&gt;
Simply add the following to your &amp;lt;code&amp;gt;WEB-INF/web.xml&amp;lt;/code&amp;gt; within the WAR archive after any &amp;lt;code&amp;gt;listener&amp;lt;/code&amp;gt; elements:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;context-param&amp;gt;&lt;br /&gt;
    &amp;lt;param-name&amp;gt;schedulerEnabled&amp;lt;/param-name&amp;gt;&lt;br /&gt;
    &amp;lt;param-value&amp;gt;false&amp;lt;/param-value&amp;gt;&lt;br /&gt;
&amp;lt;/context-param&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= You’re Good to Go! =&lt;br /&gt;
&lt;br /&gt;
You&#039;re now ready to log into the web application and start scheduling jobs! The URL you use to access Obsidian will depend on how it&#039;s been deployed, but will typically be something like http://localhost/obsidian, or http://localhost/standadminObsidianAdmin.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Default User:&#039;&#039;&#039; Each Obsidian installation using native authentication starts with a single default user named &#039;&#039;admin&#039;&#039; with password &#039;&#039;changeme&#039;&#039;. You should change this after your first log in. See [[Admin_User_Management|User Management]] for how to change a user&#039;s password.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
At this point we suggest you play around with the admin web application. Many screens offer inline help. Otherwise, you can refer to our [[Admin Web Application Guide]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To get started writing jobs that you can run in Obsidian, see [[Implementing_Jobs|Implementing Jobs]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
See the &#039;&#039;&#039;[[User Guide]]&#039;&#039;&#039; if you have questions or wish to explore what Obsidian offers.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=FAQ&amp;diff=4146</id>
		<title>FAQ</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=FAQ&amp;diff=4146"/>
		<updated>2026-07-31T14:49:51Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Some of the most common questions we receive about Obsidian are below. They range from questions about how Obsidian works, to how you go about taking advantage of different features. If you have a question not asked below, feel free to [[Contact_the_Obsidian_Scheduler_Team|contact us]]. During business hours, we usually have free support chat available which is linked on the left of the page.&lt;br /&gt;
&lt;br /&gt;
If you are having a specific issue, see [[Troubleshooting]].&lt;br /&gt;
&lt;br /&gt;
== What is Obsidian exactly? ==&lt;br /&gt;
Obsidian is a Java-based task/job scheduler. At its most basic level, it allows you to run jobs at certain recurring times, like Quartz or cron4j, but it goes well beyond what either provides. It has native, configuration-free clustering, which provides fail-over and load-sharing. In addition, it provides features like a full UI, job chaining, and event notification which are lacking in other popular schedulers.&lt;br /&gt;
&lt;br /&gt;
Obsidian consists of two main parts - a scheduler service and an admin web application which provides a UI to configure and monitor the scheduler&#039;s operation. These two features can be run together or separately (see [[Getting Started]]).&lt;br /&gt;
&lt;br /&gt;
== How do I set up Obsidian? ==&lt;br /&gt;
Check our [[Getting Started]] guide, or email us at our [https://web.obsidianscheduler.com/support/ support address].&lt;br /&gt;
&lt;br /&gt;
== Is it free? ==&lt;br /&gt;
We grant free single-node licenses which you can use forever free of charge ([https://web.obsidianscheduler.com/download/ download here]). In addition, you get a second node that you can use for one year free of charge to allow you to evaluate Obsidian clustering.&lt;br /&gt;
&lt;br /&gt;
For additional licenses, including clustering nodes, the cost starts at $649 CAD each.  For example, if you want to run a 2-node cluster, after the first year you would need to purchase a single $649 license. This license gives you access to free support and all future versions of the product.&lt;br /&gt;
&lt;br /&gt;
Obsidian also provides other licensing options which can be found on our [https://web.obsidianscheduler.com/pricing/ main site].&lt;br /&gt;
&lt;br /&gt;
== Is it open source? ==&lt;br /&gt;
Obsidian is currently not open-source. While we support the open-source movement and have contributed to it, we have chosen not to open-source Obsidian so we could help fund our development and support costs.&lt;br /&gt;
&lt;br /&gt;
== What technologies does Obsidian use? ==&lt;br /&gt;
Obsidian is currently built on Java 11, and will run on newer versions as well. The admin web application requires a servlet container, such as Tomcat or Jetty. Obsidian also requires a database such as MySQL or Oracle, where scheduling data is stored. Obsidian has a small set of [[Advanced_Configuration#Dependent_Libraries|library dependencies]] (.jar files), selected to avoid conflicts with our users&#039; own libraries. See [[Getting Started]] for details on which versions of servlet containers and database platforms are supported.&lt;br /&gt;
&lt;br /&gt;
== Why should I use Obsidian? ==&lt;br /&gt;
Obsidian provides a complete end-to-end solution for job scheduling. Often developers focus on the task in front of them and forget about the long-term costs of operations and maintenance. Job scheduling is about more than task execution - monitoring, recovery, notification, etc. are all crucial features to enterprise- or smaller-level infrastructures.&lt;br /&gt;
&lt;br /&gt;
Obsidian is designed to reduce implementation and operations costs while also facilitating development. Our team&#039;s years of experience in the field have helped us pinpoint the common issues organizations have with job schedulers and design a product that eliminates those problems.&lt;br /&gt;
&lt;br /&gt;
== How is Obsidian different from Quartz? ==&lt;br /&gt;
Quartz is mainly an embedded jar that provides you with the ability to schedule recurring jobs. Developers have to integrate it into their application and layer on code to do things like event notifications, job chaining, etc. In addition, database usage and clustering require additional configuration and, in our opinion, the available UIs are not complete. Quartz focuses primarily on the job execution side of scheduling, and provides extension points for developers to customize behaviour according to their needs. &lt;br /&gt;
&lt;br /&gt;
Conversely, Obsidian is designed from the ground-up to provide clustering, chaining, a complete UI, event notifications, real-time administration, etc. right out of the box. While Obsidian is also embeddable, it is intended to provide a complete solution to job scheduling, including monitoring, high availability, and recovery. It is designed based on years of industry experience dealing with issues of reliability and operations issues. Obsidian doesn&#039;t just make your developers more productive, it eliminates work for your whole IT department by making the information they need to troubleshoot and resolve issues quickly available at a moment&#039;s notice.&lt;br /&gt;
&lt;br /&gt;
== How does clustering work? ==&lt;br /&gt;
Obsidian&#039;s [[Clustering|clustering]] works by coordinating effort of various nodes through a central relational database such as MySQL. Each node periodically attempts to claim its share of work off of a work queue. The algorithm that performs this takes into account how many other nodes are running, and how many jobs need to be run at the current time in order to provide load balancing. That is one key difference in Obsidian since it values load-sharing over second-level precision, since most applications don&#039;t require it. Quartz, by contrast, does clustering by having all nodes  immediately try to claim a job for execution, which can result in one node claiming multiple jobs while others remain idle.&lt;br /&gt;
&lt;br /&gt;
You can add new cluster nodes at any time and they will automatically join the pool, and the load-sharing and scheduling algorithm will adapt right away.&lt;br /&gt;
&lt;br /&gt;
== What&#039;s Obsidian&#039;s performance like and how does it scale? ==&lt;br /&gt;
Whether you are running a single node or using multiple clustered nodes, Obsidian is very efficient at scheduling and execution. We have successfully run thousands of jobs a minute on basic developer hardware without issue. Typically, our users are running a tiny fraction of that number, and we haven&#039;t received any reports of poor performance due to scheduling activity.&lt;br /&gt;
&lt;br /&gt;
It is unlikely Obsidian&#039;s scheduling activity will have much of a performance impact, so most of our users are constrained by the resource requirements of their actual job code.&lt;br /&gt;
&lt;br /&gt;
Obsidian can scale to many nodes without significant performance degradation. Most customers use 1 or 2 nodes, but running 10 nodes or more should pose no issue.&lt;br /&gt;
&lt;br /&gt;
== How do I get my job application code running in the Obsidian WAR or standalone scheduler? ==&lt;br /&gt;
If you are running a Obsidian as a standalone scheduler, embedded in your application, or in the WAR with scheduling enabled, you need to ensure your application JARs are on the classpath. In the WAR, this means adding your JAR files to the &amp;lt;code&amp;gt;WEB-INF/lib&amp;lt;/code&amp;gt; directory. If you are embedding Obsidian, it will already have access to the application&#039;s classpath. If running as a standalone scheduler, you will have to add your application JARs to the &amp;lt;code&amp;gt;/lib&amp;lt;/code&amp;gt; directory. Note that you must include any JARs that contain your job classes, as well as those that contain dependent code.&lt;br /&gt;
&lt;br /&gt;
If you are running a standalone web application that does not have a scheduler running, you do not have to update its classpath with your compiled jobs, unless you are running a version older than 2.6, but there are steps you need to take which are outlined in [[Implementing_Jobs#Classpath_for_Building|Classpath for Building]].&lt;br /&gt;
&lt;br /&gt;
== How do scripting jobs work? ==&lt;br /&gt;
Just configure your job in the [[Admin Jobs|Jobs]] screen (or use the REST API) by entering the script itself and any configuration parameters. Then, Obsidian will run the script as if it&#039;s any other job. &lt;br /&gt;
&lt;br /&gt;
Note that scripts will inherently run slower than Java code, so these are better suited to ad hoc jobs or operations type tasks.&lt;br /&gt;
&lt;br /&gt;
== How can I send SMS notifications? I only see email support. ==&lt;br /&gt;
Virtually all carriers provide email addresses which will forward automatically to SMS. For example, in the United States, SMS text messages can be sent by emailing &amp;lt;code&amp;gt;domestic-number@txt.att.net&amp;lt;/code&amp;gt;. Here is a full listing of [http://en.wikipedia.org/wiki/List_of_SMS_gateways SMS gateways].&lt;br /&gt;
&lt;br /&gt;
== Why isn&#039;t my job showing up? ==&lt;br /&gt;
&lt;br /&gt;
Out of the box, Obsidian will display any previously scheduled jobs and Obsidian&#039;s bundled jobs. You can type in your class name as described in  [[Admin_Jobs#Nickname.2C_Class_and_Folder|Admin Jobs]]. &lt;br /&gt;
&lt;br /&gt;
You can take advantage of our [[Spring_Integration|Spring Integration]]. Jobs that have been wired into your &amp;lt;code&amp;gt;ApplicationContext&amp;lt;/code&amp;gt; will be available in the list.&lt;br /&gt;
&lt;br /&gt;
You can also use Obsidian&#039;s [[Implementing_Jobs#Classpath_Scanning|classpath scanning]] as of &#039;&#039;&#039;Obsidian 2.0&#039;&#039;&#039;. Note that for this feature to work, the user account running Obsidian must have read privileges to the &amp;lt;code&amp;gt;WEB-INF&amp;lt;/code&amp;gt; directory of Obsidian&#039;s deployment directory.&lt;br /&gt;
&lt;br /&gt;
== What libraries is Obsidian dependent on? ==&lt;br /&gt;
See [[Advanced_Configuration#Dependent_Libraries|Advanced Configuration]] for dependency details.&lt;br /&gt;
&lt;br /&gt;
== How do I deploy changes to my jobs? Can I hotswap JARs? ==&lt;br /&gt;
Yes you can! [[Job Forking]] needs to be enabled and configured for your deployed environment(s). Since support in Java for reloading class definitions is too unreliable to offer our users, Obsidian doesn&#039;t support it. But you can use the Job Forking feature to benefit from updates to deployed classes. Of course, this is simply for job execution. If the job&#039;s configuration support needs to change (new parameters, new description for UI, etc), you will need to deploy the change to each scheduler node and restart each one.&lt;br /&gt;
&lt;br /&gt;
== How can I deploy job changes without redeploying or restarting Tomcat? ==&lt;br /&gt;
Apart from using the [[Job Forking]] support noted above, if you want truly dynamic job changes that even support configuration additions/removals, the best approach we can offer for this is to use our embedded [[Scripting_Jobs|Scripting]] support. &lt;br /&gt;
&lt;br /&gt;
You can either use something like the included &amp;lt;code&amp;gt;GroovyJob&amp;lt;/code&amp;gt;, or write your own job that executes a job script on the file system. The latter approach lets you just replace script files on demand. If you use this approach, you can define a Java job which accepts a defined script name parameter, which you can then execute using [https://docs.groovy-lang.org/latest/html/documentation/guide-integrating.html Groovy], or one of our other supported scripting languages.&lt;br /&gt;
&lt;br /&gt;
== What level of scheduling precision does Obsidian support? ==&lt;br /&gt;
&lt;br /&gt;
Obsidian supports minute-level scheduling precision. Obsidian values a balanced load-sharing algorithm over second-level precision.&lt;br /&gt;
&lt;br /&gt;
== What about Daylight Savings Time (DST) or Summer Time? Will Obsidian handle these gracefully? ==&lt;br /&gt;
&lt;br /&gt;
We recommend against using DST time zones on scheduling servers as you will perhaps experience undesired side effects. For example, if you have a job that you want to run every hour, when a spring DST change pushes the clock back an hour, Obsidian will not execute it again since the time has repeated and it is viewed as having already run. Or you have a job scheduled to run during a window that ends up not occuring because of an autumnal time forward adjustment. If DST is absolutely required, Obsidian does handle it gracefully but we recommend you [[Contact_the_Obsidian_Scheduler_Team|contact us]] to discuss the full implications.&lt;br /&gt;
&lt;br /&gt;
== How do I change the time zone that Obsidian uses? ==&lt;br /&gt;
&lt;br /&gt;
Obsidian uses the Java timezone, which can be specified using the Java system property &amp;lt;code&amp;gt;user.timezone&amp;lt;/code&amp;gt;. For example, in Tomcat you can append &amp;lt;code&amp;gt;-Duser.timezone=Europe/Sofia&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;JAVA_OPTS&amp;lt;/code&amp;gt; in your startup script (e.g. startup.sh).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note: &#039;&#039;&#039; Changing your timezone in an existing Obsidian installation can result in undefined behaviour if it has been running recently. We suggest setting your timezone before database creation when possible.&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 4.2.0, Time Zone ID may be specified in [[Cron]] patterns themselves to request that Obsidian run the job&#039;s scheduled according to the time of the TimeZone specified regardless of the default TimeZone of the Obsidian cluster.&lt;br /&gt;
&lt;br /&gt;
== How do I upgrade from a previous version of Obsidian? ==&lt;br /&gt;
See our [[Upgrading_Obsidian|Upgrade]] page for details.&lt;br /&gt;
&lt;br /&gt;
== What does the acronym &amp;quot;XYZ&amp;quot; mean? ==&lt;br /&gt;
&lt;br /&gt;
Here are definitions of some acronyms commonly used in Obsidian&#039;s documentation.&lt;br /&gt;
&lt;br /&gt;
* DB/DBMS - [https://en.wikipedia.org/wiki/Database Database Management System]: A software application which provides storage and querying of a collection of data.&lt;br /&gt;
* SQL [https://en.wikipedia.org/wiki/SQL Structured Query Language]: A language used to query databases.&lt;br /&gt;
* API - [https://en.wikipedia.org/wiki/Application_programming_interface Application Programming Interface]: A set of code routines, protocols or tools used to build software applications.&lt;br /&gt;
* REST [https://en.wikipedia.org/wiki/Representational_state_transfer Representational State Transfer]: A style of building a web-based API using common practices which is typically available over HTTP.&lt;br /&gt;
* HTTP [https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol Hypertext Transfer Protocol]: The communication protocol used for the world wide web and most REST APIs.&lt;br /&gt;
* JRE - [https://en.wikipedia.org/wiki/Java_(software_platform)#Java_Runtime_Environment Java Runtime Environment]: Java applications run through a JRE.&lt;br /&gt;
* JDK - [https://en.wikipedia.org/wiki/Java_Development_Kit Java Development Kit ]: A set of Java tools which provide development and compilation support. Some applications require a JDK to run.&lt;br /&gt;
* JAR - [https://en.wikipedia.org/wiki/JAR_(file_format) Java Archive File ]: The standard packaging unit for Java code libraries.&lt;br /&gt;
* WAR - [https://en.wikipedia.org/wiki/WAR_(file_format) Web Application Archive ]: A way to package Java web applications which can be deployed in a standardized way.&lt;br /&gt;
* JDBC - [https://en.wikipedia.org/wiki/Java_Database_Connectivity Java Database Connectivity]: An API which provides database access to Java applications.&lt;br /&gt;
&lt;br /&gt;
== What is the default username and password? ==&lt;br /&gt;
&lt;br /&gt;
Each Obsidian installation using native authentication (the default) starts with a single default user named &#039;&#039;&#039;admin&#039;&#039;&#039; with password &#039;&#039;&#039;changeme&#039;&#039;&#039;. You should change this after your first login. See [[User Management]] for how to change a user&#039;s password.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Embedded_API&amp;diff=4145</id>
		<title>Embedded API</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Embedded_API&amp;diff=4145"/>
		<updated>2026-07-31T14:49:51Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian provides a unified Embedded API which contains all the same actions and semantics as the [[REST API]].&lt;br /&gt;
&lt;br /&gt;
This API is accessible through Java, and can be used in software environments where the [[REST API]] is unavailable or undesired. &lt;br /&gt;
&lt;br /&gt;
[https://web.obsidianscheduler.com/obsidianapi/ Javadoc] is also available to supplement this page.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The Obsidian Embedded API allows your application to access Obsidian data, schedule jobs, and control Obsidian in a variety of ways. &lt;br /&gt;
&lt;br /&gt;
The API gives users the power to do things like initialize scheduled jobs at deployment time, trigger jobs based on events in your applications, and expose pieces of the Obsidian API in their own custom user interfaces.&lt;br /&gt;
&lt;br /&gt;
== Accessing the API  ==&lt;br /&gt;
&lt;br /&gt;
The Embedded API is written in Java and can be be accessed within the context of a running Obsidian node, although the scheduler itself need not be active. You can even call API operations from within jobs, which you can use to get custom chaining or workflow behaviour, or update configuration on certain types of events detected in a job.&lt;br /&gt;
&lt;br /&gt;
In addition, it can be embedded in applications that are not running Obsidian, simply by including Obsidian&#039;s [[Advanced_Configuration#Dependent_Libraries|dependent libraries]] and [[Advanced_Configuration#Properties_File|properties file]].&lt;br /&gt;
&lt;br /&gt;
==Authorization==&lt;br /&gt;
&lt;br /&gt;
The Embedded API is &#039;&#039;&#039;not secured&#039;&#039;&#039;, though all actions accept an audit user which is linked to actions which modify Obsidian state. Any code that has access to the Obsidian database via its defined credentials can manipulate Obsidian job configurations.&lt;br /&gt;
&lt;br /&gt;
== Transactions ==&lt;br /&gt;
&lt;br /&gt;
By default, when you invoke actions within the API, each individual call is in its own transaction which is committed when successful, and rolls back on failure.&lt;br /&gt;
&lt;br /&gt;
However, you can wrap multiple API calls into a single transaction, so you get one unit of work which either is fully committed or rolled back. An example is shown below. For full semantics, see [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/AbstractAPIManager.html#withTransaction(java.lang.String,java.util.concurrent.Callable) AbstractAPIManager.withTransaction].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
final String auditUser = &amp;quot;Bob&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
List&amp;amp;lt;HostDetail&amp;amp;gt; updated = new HostManager().withTransaction(auditUser, new Callable&amp;amp;lt;&amp;amp;lt;HostDetail&amp;amp;gt;&amp;amp;gt;() {&lt;br /&gt;
    &lt;br /&gt;
    public List&amp;amp;lt;HostDetail&amp;amp;gt; call() throws Exception {&lt;br /&gt;
        &lt;br /&gt;
        // disable some hosts in an atomic manner (does not have to be the same instance of even type of Manager class)&lt;br /&gt;
        HostDetail hostA = new HostManager().updateHost(&amp;quot;hostA&amp;quot;, new HostUpdateRequest().withEnabled(false), auditUser );&lt;br /&gt;
        HostDetail hostB = new HostManager().updateHost(&amp;quot;hostB&amp;quot;, new HostUpdateRequest().withEnabled(false), auditUser );&lt;br /&gt;
            &lt;br /&gt;
        // this is within the same transaction&lt;br /&gt;
        new JobManager().resubmitRuntime(jobToResubmit, auditUser);&lt;br /&gt;
&lt;br /&gt;
        return Arrays.asList(hostA, hostB);&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
     &lt;br /&gt;
System.out.println(&amp;quot;Updated hosts: &amp;quot; + updated);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==API Basics ==&lt;br /&gt;
The API is exposed through &amp;lt;code&amp;gt;Manager&amp;lt;/code&amp;gt; classes in the package [https://web.obsidianscheduler.com/obsidianapi/index.html?com/carfey/ops/api/embedded/package-summary.html com.carfey.ops.api.embedded]:&lt;br /&gt;
* &amp;lt;code&amp;gt;JobManager&amp;lt;/code&amp;gt; - Create and manage jobs, job conflicts and chaining.&lt;br /&gt;
* &amp;lt;code&amp;gt;RuntimeManager&amp;lt;/code&amp;gt; - List job runtime history, resubmit jobs, preview scheduled times, etc.&lt;br /&gt;
* &amp;lt;code&amp;gt;HostManager&amp;lt;/code&amp;gt; - Manage and list active scheduling hosts (i.e. nodes).&lt;br /&gt;
* &amp;lt;code&amp;gt;CustomCalendarManager&amp;lt;/code&amp;gt; - Manage custom calendars.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To invoke an API method, simply create a new Manager instance, and invoke the appropriate method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
RuntimeListing recentRuntimes = new RuntimeManager().listRuntimes(new RuntimeListingParameters());&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can reuse manager instances if you like, but it is ultimately up to your preference.&lt;br /&gt;
&lt;br /&gt;
=== Exceptions ===&lt;br /&gt;
API calls generally throw three types of exceptions:&lt;br /&gt;
&lt;br /&gt;
* [https://web.obsidianscheduler.com/obsidianapi/com/carfey/suite/action/ValidationException.html ValidationException] - some sort of validation error occurred.&lt;br /&gt;
* [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] - The requested entity could not be found by the ID supplied.&lt;br /&gt;
* All other Exception types - some other type of error that you probably can&#039;t do much with (database issues, etc.).&lt;br /&gt;
&lt;br /&gt;
When integrating the API into your application, you may wish to apply appropriate handling to &amp;lt;code&amp;gt;ValidationException&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;MissingEntityException&amp;lt;/code&amp;gt;, but otherwise you can treat exceptions as a generic server errors.&lt;br /&gt;
&lt;br /&gt;
== Enumerations ==&lt;br /&gt;
&lt;br /&gt;
Below are valid values for commonly used fields in the API. These are also used by the [[REST API]]. &lt;br /&gt;
&lt;br /&gt;
Corresponding Java enums can be found in the package [https://web.obsidianscheduler.com/obsidianapi/index.html?com/carfey/ops/api/enums/package-summary.html com.carfey.ops.api.enums].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job [[Admin_Jobs#State|State]]&#039;&#039;&#039; &lt;br /&gt;
* &amp;lt;code&amp;gt;ENABLED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;DISABLED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;UNSCHEDULED_ACTIVE&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;CHAIN_ACTIVE&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;AD_HOC_ACTIVE&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job History [[Job_Features#Execution_Statuses | Status]]&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;READY&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;RUNNING&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;COMPLETED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;FAILED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;MISSED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;DIED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;CONFLICTED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;OVERLAPPED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ABANDONED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;CONFLICT_MISSED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;CHAIN_SKIPPED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;PENDING&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Submission Mode &#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;CHAIN_SUBMISSION&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;CONFLICTED_RECOVERY&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job [[Job_Features#Recovery | Recovery Type]]&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;NONE&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ALL&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;CONFLICTED&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Parameter Type&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;STRING&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;INTEGER&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;LONG&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;DECIMAL&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;BOOLEAN&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;CLASS&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Chain [[Job_Features#Execution_Statuses | Status]]&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;ABANDONED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;COMPLETED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;CONDITIONAL&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;DIED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;FAILED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;MISSED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;OVERLAPPED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;CHAIN_SKIPPED&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Chain/Conditional Job Notification Operator&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;EQUALS&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;NOT_EQUALS&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;IN&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;NOT_IN&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;EXISTS&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;NOT_EXISTS&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;REGEXP&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;STARTS_WITH&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ENDS_WITH&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;CONTAINS&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;GREATER_THAN&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;LESS_THAN&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;GREATER_THAN_OR_EQUAL&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;LESS_THAN_OR_EQUAL&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Subscription/Log [[Event_Notifications#Category-Based_Subscriptions | Category]]&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;DASHBOARD&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;DISPATCH&amp;lt;/code&amp;gt; &lt;br /&gt;
* &amp;lt;code&amp;gt;JOB&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;JOB_CHAIN&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;JOB_CONFIG&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;JOB_QUEUER&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;JOB_RECOVERY&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;JOB_RUN&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;JOB_SPAWNER&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;LICENCE&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;SYSTEM_PARAMETER&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;QUEUE&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Subscription/Log Level&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;FATAL&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ERROR&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;WARNING&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;INFO&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TRACE&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;User Role&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;ADMIN&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;WRITE&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;OPERATOR&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;AUTHOR&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;API&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;LIMITED_READ&amp;lt;/code&amp;gt;&lt;br /&gt;
As of &#039;&#039;Obsidian 5.0.0&#039;&#039;, these are class constants instead of enums to support [[Admin_User_Management#Job_Folder_Rights|dynamic roles]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Subscription Job Status&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;COMPLETED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;CONDITIONAL&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;DIED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;FAILED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;RECOVERY&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sort Direction&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;ASC&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;DESC&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Event Hook Action&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;PAUSE&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;RESUME&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Stats [[Job_Features#Execution_Statuses | Status]]&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;COMPLETED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;FAILED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;MISSED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;DIED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;OVERLAPPED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ABANDONED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;CONFLICT_MISSED&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;CHAIN_SKIPPED&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Stats Unit&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;SECONDS&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;MINUTES&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;HOURS&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Stats Duration&#039;&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;DAY&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TWO_DAY&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;THREE_DAY&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;FIVE_DAY&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;WEEK&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;MONTH&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;TWO_MONTH&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;THREE_MONTH&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;SIX_MONTH&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;YEAR&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JobManager API =&lt;br /&gt;
&lt;br /&gt;
[https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/JobManager.html JobManager] is used to manage job configurations, conflicts and chaining. If you are looking for ways to view or manage job runtimes, see [[#RuntimeManager API|RuntimeManager]].&lt;br /&gt;
&lt;br /&gt;
== List Jobs ==&lt;br /&gt;
&lt;br /&gt;
You can list or search existing jobs using [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/JobManager.html#listJobs(com.carfey.ops.api.bean.job.JobListingParameters) JobManager.listJobs()], which accepts an optional [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobListingParameters.html JobListingParameters] and returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobListing.html JobListing]. &lt;br /&gt;
&lt;br /&gt;
You can limit the returned jobs by providing a &amp;lt;code&amp;gt;JobListingParameters&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;JobListingParameters Fields&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|-&lt;br /&gt;
| activeStatuses || N || Restricts the preview to the selected a valid [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/JobStatus.html JobStatus] values&lt;br /&gt;
|-&lt;br /&gt;
| effectiveDate || N || If querying by activeStatuses, this allows you to indicate what point in time to compare against the job status. Defaults to next minute.&lt;br /&gt;
|-&lt;br /&gt;
| hosts || N || If specified, only jobs that run on the specified host name(s) are included.&lt;br /&gt;
|-&lt;br /&gt;
| nicknames || N || If specified, only jobs matching the supplied nicknames are returned. Wildcards may be included to support partial matches by using %, or exact literals can be used. For example, to find all jobs containing the word &amp;quot;order&amp;quot;, use &amp;quot;%order%&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| jobClasses || N || If specified, only jobs matching the supplied job class are returned. Wildcards may be included to support partial matches by using %, or exact literals can be used. For example, to find all jobs with job classes containing the word &amp;quot;Export&amp;quot;, use &amp;quot;%export%&amp;quot;. Supports multiple values. &#039;&#039;Available from version 3.4.0 forward.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| folders || N || If specified, only jobs matching the supplied folders are returned. If a parent path is supplied, all jobs containing that path or subpaths are included in the results.  If an empty string or null is supplied, jobs with no folder will be returned. Supports multiple values. &#039;&#039;Available from version 4.1.0 forward.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| filterParameters || N || If specified, values supplied in this map can be used to match on job parameter values, either custom or defined. If multiple values for the same name (i.e. key) are supplied, a job is matched if any of its configured values match one of the supplied values. If multiple names are used, each must have a matching value for the job to be returned.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Usage note:&#039;&#039;&#039; This can be used to tag jobs with searchable metadata by configuring custom parameters. For example, if jobs belong to logical groups, you may create a custom parameter on applicable jobs named &amp;quot;group&amp;quot; and use &#039;&#039;filterParameters&#039;&#039; to locate jobs belonging to specific groups, such as &amp;quot;customer&amp;quot; or &amp;quot;order&amp;quot;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_jobs|GET a list of jobs]]&lt;br /&gt;
&lt;br /&gt;
== Get a Job&#039;s Details ==&lt;br /&gt;
&lt;br /&gt;
To get full job information, including all historical schedules and parameter information, call [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/JobManager.html#getJob(long) JobManager.getJob()] with the appropriate job ID. The method will return a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobDetail.html JobDetail], or throw a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if it does not exist. &lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_details_of_an_existing_job|GET details of an existing job]]&lt;br /&gt;
&lt;br /&gt;
== Add a Job ==&lt;br /&gt;
&lt;br /&gt;
You can add a new job using [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/JobManager.html#addJob(com.carfey.ops.api.bean.job.JobCreationRequest,java.lang.String) JobManager.addJob()], which accepts a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobCreationRequest.html JobCreationRequest]. The method will return the job as a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobDetail.html JobDetail].&lt;br /&gt;
&lt;br /&gt;
When you create the job, you provide an initial schedule. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; When supplying effective and end dates for schedules, seconds must be omitted. To do so, use &amp;lt;code&amp;gt;com.carfey.jdk.lang.DateTime.clearSeconds()&amp;lt;/code&amp;gt;, which returns a copy of the date with seconds removed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;JobCreationRequest Fields &#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| jobClass || Y || Fully qualified class name of the job. Max 255 chars.&lt;br /&gt;
|- &lt;br /&gt;
| nickname || Y || Unique nickname for the job. Max 255 chars.&lt;br /&gt;
|- &lt;br /&gt;
| folder|| N || Folder in which to place the job. Paths are separated by forward-slashes. Back-slashes are automatically converted to forward-slashes, and leading or trailing slashes are removed. Max 255 chars. &#039;&#039;Available from version 4.1.0 forward.&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| pickupBufferMinutes || N || Pickup buffer minutes which defaults to 2. Integer greater than zero.&lt;br /&gt;
|- &lt;br /&gt;
| recoveryType || Y || The [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/JobRecoveryType.html JobRecoveryType] to use for the job.&lt;br /&gt;
|- &lt;br /&gt;
| state || Y || Initial schedule&#039;s [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/JobStatus.html JobStatus].&lt;br /&gt;
|- &lt;br /&gt;
| schedule || Y/N || If state is &amp;lt;code&amp;gt;ENABLED&amp;lt;/code&amp;gt;, the mandatory cron-style schedule for the job. If not &amp;lt;code&amp;gt;ENABLED&amp;lt;/code&amp;gt;, this should be omitted. You may specify multiple cron patterns delimiting them with a semi-colon.&lt;br /&gt;
|- &lt;br /&gt;
| effectiveDate || N || Optional effective date for the initial schedule, with no seconds specified. If not set, this defaults to next minute. Until this date is reached, the job is &amp;lt;code&amp;gt;DISABLED&amp;lt;/code&amp;gt;.&lt;br /&gt;
|- &lt;br /&gt;
| endDate || N || Optional end date for the initial schedule, with no seconds specified. If set, the job will become &amp;lt;code&amp;gt;DISABLED&amp;lt;/code&amp;gt; after this date passes.&lt;br /&gt;
|- &lt;br /&gt;
| customCalendarId || N || Optional custom calendar id.&lt;br /&gt;
|- &lt;br /&gt;
| hosts || N || Zero or more host names that this job may run on. If none set, the job may run on any host.&lt;br /&gt;
|- &lt;br /&gt;
| minExecutionDuration || N || The minimum expected job runtime. Format is an integer greater than zero immediately followed by &amp;quot;s&amp;quot;, &amp;quot;m&amp;quot; or &amp;quot;h&amp;quot;. Example: &amp;quot;15m&amp;quot;.&lt;br /&gt;
|- &lt;br /&gt;
| maxExecutionDuration || N ||  The maximum expected job runtime. Format is an integer greater than zero immediately followed by &amp;quot;s&amp;quot;, &amp;quot;m&amp;quot; or &amp;quot;h&amp;quot;. Example: &amp;quot;15m&amp;quot;.&lt;br /&gt;
|- &lt;br /&gt;
| autoInterrupt || N ||  Boolean indicating whether auto interrupt functionality is desired. May only be true when the job is an interruptable job and a maxExecutionDuration has been specified. Defaults to false.&lt;br /&gt;
|- &lt;br /&gt;
| autoRetryCount|| N || Number of auto retries on non-interrupted execution failure. Defaults to 0, which means none are desired.&lt;br /&gt;
|- &lt;br /&gt;
| autoRetryInterval|| N || Minimum number of minutes between auto retries - calculated from failure time. Defaults to 0 and indicates try at next available opportunity.&lt;br /&gt;
|- &lt;br /&gt;
| autoRetryIntervalExponent|| N || Boolean indicating whether to exponentially increase interval time between retries.&lt;br /&gt;
|- &lt;br /&gt;
| chainAll || N || Boolean indicating whether all chained instances are triggered when job is currently running. Otherwise, only one newly chained record is created. Defaults to false.&lt;br /&gt;
|- &lt;br /&gt;
| parameters || Y/N || Zero or more parameter definitions. If a job defines required parameters with the &amp;lt;code&amp;gt;@Configuration&amp;lt;/code&amp;gt; annotation, a job will fail to create unless they are supplied. Otherwise, this field is optional. Parameter definitions must have values for &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;value&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; is a valid [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/ParameterType.html ParameterType]. To define multiple values for a single parameter name, simply include multiple items in the parameters collection. Values may contain global parameter references (e.g. &amp;lt;code&amp;gt;&amp;amp;#123;&amp;amp;#123;globalParameterName&amp;amp;#125;&amp;amp;#125;&amp;lt;/code&amp;gt;).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#POST_a_new_job|POST a new job]]&lt;br /&gt;
&lt;br /&gt;
== Update a Job ==&lt;br /&gt;
&lt;br /&gt;
You can update an existing job using [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/JobManager.html#updateJob(long,com.carfey.ops.api.bean.job.JobUpdateRequest,java.lang.String) JobManager.updateJob()], which accepts a job ID and a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobUpdateRequest.html JobUpdateRequest]. The method will return the new state of the job as a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobDetail.html JobDetail], or throw a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if it does not exist. &lt;br /&gt;
&lt;br /&gt;
This does not support schedule changes or additions. For schedule changes, see [[#Add_a_New_Schedule_to_a_Job|Add a New Schedule to a Job]].&lt;br /&gt;
&lt;br /&gt;
This method will only update fields that are supplied in the &amp;lt;code&amp;gt;JobUpdateRequest&amp;lt;/code&amp;gt;. You may update one or more fields as desired.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;JobUpdateRequest Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| jobClass || N || Fully qualified class name of the job. Max 255 chars.&lt;br /&gt;
|- &lt;br /&gt;
| nickname || N || Unique nickname for the job. Max 255 chars.&lt;br /&gt;
|- &lt;br /&gt;
| folder|| N || Folder in which to place the job. Paths are separated by forward-slashes. Back-slashes are automatically converted to forward-slashes, and leading or trailing slashes are removed. Max 255 chars. &#039;&#039;Available from version 4.1.0 forward.&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| pickupBufferMinutes || N || Pickup buffer minutes. Integer greater than zero.&lt;br /&gt;
|- &lt;br /&gt;
| recoveryType || N || The [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/JobRecoveryType.html JobRecoveryType] to use for the job.&lt;br /&gt;
|- &lt;br /&gt;
| hosts || N || Zero or more host names that this job may run on. If none set, the job may run on any host.&lt;br /&gt;
|- &lt;br /&gt;
| minExecutionDuration || N || The minimum expected job runtime. Format is an integer greater than zero immediately followed by &amp;quot;s&amp;quot;, &amp;quot;m&amp;quot; or &amp;quot;h&amp;quot;. Example: &amp;quot;15m&amp;quot;.&lt;br /&gt;
|- &lt;br /&gt;
| maxExecutionDuration || N ||  The maximum expected job runtime. Format is an integer greater than zero immediately followed by &amp;quot;s&amp;quot;, &amp;quot;m&amp;quot; or &amp;quot;h&amp;quot;. Example: &amp;quot;2h&amp;quot;.&lt;br /&gt;
|- &lt;br /&gt;
| autoInterrupt || N ||  Boolean indicating whether auto interrupt functionality is desired. May only be true when the job is an interruptable job and a maxExecutionDuration has been specified. Defaults to false.&lt;br /&gt;
|- &lt;br /&gt;
| autoRetryCount || N || Number of auto retries on non-interrupted execution failure. 0 if none are desired.&lt;br /&gt;
|- &lt;br /&gt;
| chainAll || N || Boolean indicating whether all chained instances are triggered when job is currently running. Otherwise, only one newly chained record is created.&lt;br /&gt;
|- &lt;br /&gt;
| parameters || N || Zero or more parameter definitions. If a job defines required parameters with the &amp;lt;code&amp;gt;@Configuration&amp;lt;/code&amp;gt; annotation, a job will fail to create unless they are supplied. Otherwise, this field is optional. Note that a null value will result in existing parameters being preserved, while an empty list is considered replacing existing parameters. Parameter definitions must have values for &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;value&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; is a valid [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/ParameterType.html ParameterType]. To define multiple values for a single parameter name, simply include multiple items in the parameters collection. Values may contain global parameter references (e.g. &amp;lt;code&amp;gt;&amp;amp;#123;&amp;amp;#123;globalParameterName&amp;amp;#125;&amp;amp;#125;&amp;lt;/code&amp;gt;).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#PUT_updates_to_an_existing_job|PUT updates to an existing job]]&lt;br /&gt;
&lt;br /&gt;
== Delete a Job ==&lt;br /&gt;
&lt;br /&gt;
You can delete an existing job using [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/JobManager.html#deleteJob(long,boolean,java.lang.String) JobManager.deleteJob()], which accepts a job ID and  &#039;&#039;cascade&#039;&#039; flag. The method will return the final state of the job before deletion as a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobDetail.html JobDetail], or throw a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if it does not exist. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Parameters&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| cascade|| Y || If set to true, all job conflict and chain definitions for this job will also be deleted. If set to false, any existing job conflicts or chain definitions will cause the request to fail.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#DELETE_an_existing_job|DELETE an existing job]]&lt;br /&gt;
&lt;br /&gt;
== List a Job&#039;s Schedules ==&lt;br /&gt;
&lt;br /&gt;
You can access the full history of a job&#039;s schedules via [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/JobManager.html#listJobSchedules(long) JobManager.listJobSchedules()] which accepts an optional [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobScheduleListingParameters.html JobScheduleListingParameters] and returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobScheduleListing.html JobScheduleListing].&lt;br /&gt;
&lt;br /&gt;
Note that the full schedule history can also be obtained via the schedules field of the [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobDetail.html JobDetail] returned by [[#Get_a_Job&#039;s_Details|JobManager.getJob()]].&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_an_existing_job.27s_schedules|GET a list of an existing job&#039;s schedules]]&lt;br /&gt;
&lt;br /&gt;
== Add a New Schedule to a Job ==&lt;br /&gt;
&lt;br /&gt;
You can add a new schedule to a job via [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/JobManager.html#addJobSchedule(long,com.carfey.ops.api.bean.schedule.ScheduleCreationRequest,java.lang.String) JobManager.addJobSchedule()], which accepts a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/schedule/ScheduleCreationRequest.html ScheduleCreationRequest]. &lt;br /&gt;
&lt;br /&gt;
This may be used to immediately change a job&#039;s scheduling state, or to schedule a future change. Creating a new schedule automatically splits and merges existing schedules. For example, if you have an enabled job and you disabled it for a day, the job will automatically re-enable after that day.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; When supplying effective and end dates for schedules, seconds must be omitted. To do so, use &amp;lt;code&amp;gt;com.carfey.jdk.lang.DateTime.clearSeconds()&amp;lt;/code&amp;gt;, which returns a copy of the date with seconds removed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ScheduleCreationRequest Fields &#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| state || Y || The schedule&#039;s [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/JobStatus.html JobStatus].&lt;br /&gt;
|- &lt;br /&gt;
| schedule || Y/N || If state is &amp;lt;code&amp;gt;ENABLED&amp;lt;/code&amp;gt;, the mandatory cron-style schedule for the job. If not &amp;lt;code&amp;gt;ENABLED&amp;lt;/code&amp;gt;, this should be omitted. You may specify multiple cron patterns delimiting them with a semi-colon.&lt;br /&gt;
|- &lt;br /&gt;
| effectiveDate || N || Optional effective date for the schedule, with no seconds specified. If not set, this defaults to next minute. Until this date is reached, the job is &amp;lt;code&amp;gt;DISABLED&amp;lt;/code&amp;gt;.&lt;br /&gt;
|- &lt;br /&gt;
| endDate || N || Optional end date for the schedule, with no seconds specified. If set, the job will become &amp;lt;code&amp;gt;DISABLED&amp;lt;/code&amp;gt; after this date passes.&lt;br /&gt;
|- &lt;br /&gt;
| customCalendarId || N || Optional custom calendar for schedule.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#POST_a_new_schedule_to_an_existing_job|POST a new schedule to an existing job]]&lt;br /&gt;
&lt;br /&gt;
== List Job Chains ==&lt;br /&gt;
&lt;br /&gt;
You can list or search existing job chains using [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/JobManager.html#listChains(com.carfey.ops.api.bean.job.JobChainListingParameters) JobManager.listChains()], which accepts an optional [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobChainListingParameters.html JobChainListingParameters] and returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobChainListing.html JobChainListing]. &lt;br /&gt;
&lt;br /&gt;
You can limit the returned job chains by providing a &amp;lt;code&amp;gt;JobChainListingParameters&amp;lt;/code&amp;gt; instance with the fields below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;JobChainListingParameters Fields&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|-&lt;br /&gt;
| active || N || Limits results to those matching the active flag (true/false).&lt;br /&gt;
|-&lt;br /&gt;
| sourceJobId || N || Limits results to those matching the supplied source job ID.&lt;br /&gt;
|-&lt;br /&gt;
| targetJobId || N || Limits results to those matching the supplied target job ID.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_job_chains| GET a list of job chains]]&lt;br /&gt;
&lt;br /&gt;
== Add a Job Chain ==&lt;br /&gt;
&lt;br /&gt;
You can add a new job chain using [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/JobManager.html#addChain(com.carfey.ops.api.bean.job.JobChainUpdateRequest,java.lang.String) JobManager.addChain()], which accepts a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobChainUpdateRequest.html JobChainUpdateRequest]. The method will return  a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobChain.html JobChain].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;JobChainUpdateRequest Fields&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| sourceJobId || Y || ID of the source job.&lt;br /&gt;
|- &lt;br /&gt;
| targetJobId || Y || ID of the target job to chain&lt;br /&gt;
|- &lt;br /&gt;
| schedule || N || Optional schedule that constrains when the job chain triggers.&lt;br /&gt;
|- &lt;br /&gt;
| triggerStates || Y || One or more [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/JobChainStatus.html JobChainStatus]. Note that &amp;lt;code&amp;gt;CONDITIONAL&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;COMPLETED&amp;lt;/code&amp;gt; types cannot be used on the same chain.&lt;br /&gt;
|- &lt;br /&gt;
| resultConditions || Y/N || Conditions based on job results that apply to the &amp;lt;code&amp;gt;CONDITIONAL&amp;lt;/code&amp;gt; trigger state. Must be supplied only when that state is used, in which case at least one condition must be supplied. Result conditions consist of a &amp;lt;code&amp;gt;variableName&amp;lt;/code&amp;gt;, an &amp;lt;code&amp;gt;operator&amp;lt;/code&amp;gt; as a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/JobChainConditionOperator.html JobChainConditionOperator], and for most operators, a list of &amp;lt;code&amp;gt;values&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;EXISTS&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;NOT EXISTS&amp;lt;/code&amp;gt; operators do not use values, so they must not be supplied. Operators &amp;lt;code&amp;gt;IN&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;NOT IN&amp;lt;/code&amp;gt; support one or more values, and all other operators accept a single value in the &amp;lt;code&amp;gt;values&amp;lt;/code&amp;gt; list.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#POST_a_new_job_chain| POST a new job chain]]&lt;br /&gt;
&lt;br /&gt;
== Update a Job Chain ==&lt;br /&gt;
&lt;br /&gt;
You can add update an existing job chain using [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/JobManager.html#updateChain(long,com.carfey.ops.api.bean.job.JobChainUpdateRequest,java.lang.String) JobManager.updateChain()], which accepts a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobChainUpdateRequest.html JobChainUpdateRequest]. The method will return the updated [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobChain.html JobChain].&lt;br /&gt;
&lt;br /&gt;
The format of &amp;lt;code&amp;gt;JobChainUpdateRequest&amp;lt;/code&amp;gt; is identical to that of [[#Add_a_Job_Chain|Add a Job Chain]].&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#PUT_updates_to_an_existing_job_chain| PUT updates to an existing job chain]]&lt;br /&gt;
&lt;br /&gt;
== Delete a Job Chain ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can delete an existing job chain using [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/JobManager.html#deleteChain(long,java.lang.String) JobManager.deleteChain()]. The method will return the final state of the job chain before deletion as a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobChain.html JobChain], or throw a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if it does not exist.&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#DELETE_an_existing_job_chain| DELETE an existing job chain]]&lt;br /&gt;
&lt;br /&gt;
== List Conflicts ==&lt;br /&gt;
&lt;br /&gt;
You can access all configured job conflict configuration via [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/JobManager.html#listConflicts() JobManager.listConflicts()], which will return [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/ConflictListing.html ConflictListing].&lt;br /&gt;
&lt;br /&gt;
The return value includes all job conflict sets, with each set in order of priority. Non-conflicted jobs are also returned.&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_job_conflicts| GET a list of job conflicts]]&lt;br /&gt;
&lt;br /&gt;
== Update Conflicts ==&lt;br /&gt;
&lt;br /&gt;
To update the configured job conflict sets, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/JobManager.html#updateConflicts(com.carfey.ops.api.bean.job.ConflictUpdateRequest,java.lang.String) JobManager.updateConflicts()] with a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/ConflictUpdateRequest.html ConflictUpdateRequest] which will return the updated [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/ConflictListing.html ConflictListing].&lt;br /&gt;
&lt;br /&gt;
The request replaces the current job conflict configuration with the supplied configuration. The return value includes all job conflict sets, with each set in order of priority. Non-conflicted jobs are also returned.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ConflictUpdateRequest Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| conflicts || N || A list of ordered sets containing job IDs. Each inner set contains jobs that conflict with each other, in order of execution precedence. Jobs that do not conflict with any other jobs are simply omitted from this list. A job can exist in multiple conflict sets, but should only occur in a particular set once. To remove all job conflicts, an empty list can be supplied for this field.&lt;br /&gt;
&lt;br /&gt;
When selecting available non-conflicted jobs to run, Obsidian inspects the conflict sets in the order provided when they are saved, and selects the highest priority available job before moving onto the next conflict set. When priority is significant and varies across different sets, ensure your conflict sets are in the desired order.  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#PUT_updates_to_job_conflicts| PUT updates to job conflicts]]&lt;br /&gt;
&lt;br /&gt;
== List a Specific Job&#039;s Conflicts ==&lt;br /&gt;
&lt;br /&gt;
You can return conflicts for a specific job with [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/JobManager.html#listJobConflicts(long) JobManager.listJobConflicts(long)], which will return [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobConflictListing.html JobConflictListing].&lt;br /&gt;
&lt;br /&gt;
Conflicting jobs are returned in priority order, including the job for which this request was made, in order that its priority within the set can be determined. Note that if the job has no conflicts, the returned conflicting jobs list will be empty, and will not contain the requested job.&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_conflicts_for_a_specific_job| GET a list of conflicts for a specific job]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Update Global Parameters ==&lt;br /&gt;
&lt;br /&gt;
To update the configured global parameters, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/JobManager.html#updateGlobalParameters(com.carfey.ops.api.bean.job.GlobalParameterUpdateRequest,java.lang.String) JobManager.updateGlobalParameters()] with a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/GlobalParameterUpdateRequest.html GlobalParameterUpdateRequest] which will return the updated [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/GlobalParameterListing.html GlobalParameterListing].&lt;br /&gt;
&lt;br /&gt;
The request replaces the current global parameters with the supplied configuration. Supplying an empty list of parameters will result in all global parameters being deleted.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Calls to remove or alter global parameters may fail if jobs that use them do not pass parameter validation as a result of the change. This can be caused by removing a referenced global parameter or values that cannot be interpreted as the appropriate type in a job.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;GlobalParameterListing Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| parameters || N || Zero or more [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/GlobalParameter.html GlobalParameter] instances. Each parameter must have values for &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;values&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; is a valid [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/ParameterType.html ParameterType].&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#PUT_updates_to_global_parameters| PUT updates to global parameters]]&lt;br /&gt;
&lt;br /&gt;
== List Global Parameters ==&lt;br /&gt;
&lt;br /&gt;
You can list all configured global parameters using [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/JobManager.html#listGlobalParameters() listGlobalParameters()], which will return a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/GlobalParameterListing.html GlobalParameterListing].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_configured_global_parameters| GET a list of configured global parameters]]&lt;br /&gt;
&lt;br /&gt;
== List Job Folders ==&lt;br /&gt;
&lt;br /&gt;
You can list all used job folders using [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/JobManager.html#listJobFolders() listJobFolders()], which will return a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/job/JobFolderListing.html JobFolderListing].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_job_folders| GET a list of job folders]]&lt;br /&gt;
&lt;br /&gt;
= RuntimeManager API =&lt;br /&gt;
&lt;br /&gt;
[https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/RuntimeManager.html RuntimeManager] is used to manage and view scheduled job runtimes (i.e. history). If you are looking for ways to view or manage jobs, see [[#JobManager API|JobManager]].&lt;br /&gt;
&lt;br /&gt;
== List Scheduled Runtimes ==&lt;br /&gt;
&lt;br /&gt;
To get a list  of scheduled or completed job runtimes (i.e. history), use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/RuntimeManager.html#listRuntimes(com.carfey.ops.api.bean.history.RuntimeListingParameters) RuntimeManager.listRuntimes()], which accepts an optional [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/history/RuntimeListingParameters.html RuntimeListingParameters] instance. The results in the returned [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/history/RuntimeListing.html RuntimeListing] are ordered by scheduled time descending, unless overridden by the &amp;lt;code&amp;gt;sort&amp;lt;/code&amp;gt; parameter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt; field in the &amp;lt;code&amp;gt;RuntimeListing&amp;lt;/code&amp;gt; indicates that there were too many results to return (i.e. exceeded &amp;lt;code&amp;gt;maxRecords&amp;lt;/code&amp;gt; as configured in the [[Admin Scheduler Settings|scheduler settings]] screen, or the &amp;lt;code&amp;gt;quantity&amp;lt;/code&amp;gt; parameter). To fetch the next page of results, invoke the same method with the &amp;lt;code&amp;gt;startKey&amp;lt;/code&amp;gt; field on &amp;lt;code&amp;gt;RuntimeListingParameters&amp;lt;/code&amp;gt; set to the returned &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RuntimeListingParameters Fields&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| jobIds || N || Restricts the search to the selected jobs. &lt;br /&gt;
|-&lt;br /&gt;
| statuses || N || Restricts the search to the selected [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/JobRuntimeStatus.html JobRuntimeStatus] values.&lt;br /&gt;
|-&lt;br /&gt;
| hosts || N || If specified, only job runtimes that are assigned to the specified host(s) are included. &lt;br /&gt;
|-&lt;br /&gt;
| folders|| N || If specified, only jobs matching the supplied folders are returned. If a parent path is supplied, all jobs containing that path or subpaths are included in the results. If an empty string is supplied, jobs with no folder will be returned. Supports multiple values. &#039;&#039;Available from version 4.1.0 forward.&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| startDate || N || Start date for the job runtimes to return (inclusive). Defaults to 24 hours ago. &lt;br /&gt;
|- &lt;br /&gt;
| endDate || N || End date for the job runtimes to return (inclusive). Defaults to a day after the start time. Must be after the start time.&lt;br /&gt;
|- &lt;br /&gt;
| startKey || N || If requesting the next page of results from a previous call, set it to the returned &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
|- &lt;br /&gt;
| quantity || N || Indicates the maximum number of results to return. This overrides the &amp;lt;code&amp;gt;maxRecords&amp;lt;/code&amp;gt; as configured in the  [[Admin Scheduler Settings|scheduler settings]] screen.&lt;br /&gt;
|- &lt;br /&gt;
| sort || N || A valid [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/SortDirection.html SortDirection], which controls the ordering of returned results. In all cases, the job scheduled time is used to sort results.&lt;br /&gt;
|-&lt;br /&gt;
| filterParameters || N || If specified, values supplied in this map can be used to match on runtime parameter values (not job-level parameters). If multiple values for the same name (i.e. key) are supplied, a runtime is matched if any of its configured values match one of the supplied values. If multiple names are used, each must have a matching value for the runtime to be returned.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_scheduled_runtimes_.28supports_multiple_jobs.29|GET a list of scheduled runtimes]]&lt;br /&gt;
&lt;br /&gt;
== Get Details on a Scheduled or Completed Runtime ==&lt;br /&gt;
&lt;br /&gt;
To get full details on a scheduled or completed job runtime, including job results and one-time run configuration, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/RuntimeManager.html#getRuntime(long) RuntimeManager.getRuntime()]. The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/history/RuntimeResult.html RuntimeResult], which has a nested [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/history/RuntimeDetail.html RuntimeDetail] containing the full job output and one-time run parameters, or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if it does not exist. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_details_of_an_existing_scheduled_.28or_completed.29_job_runtime|GET details of an existing scheduled or completed runtime]]&lt;br /&gt;
&lt;br /&gt;
== Preview Runtimes ==&lt;br /&gt;
&lt;br /&gt;
To preview future runtimes for one or more jobs, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/RuntimeManager.html#listRuntimePreviews(com.carfey.ops.api.bean.schedule.RuntimePreviewParameters) RuntimeManager.listRuntimePreview()], which accepts an optional [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/schedule/RuntimePreviewParameters.html RuntimePreviewParameters] instance to filter the results. The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/schedule/RuntimePreviewListing.html RuntimePreviewListing]. &lt;br /&gt;
&lt;br /&gt;
This method is useful to see when jobs will run during a given time period. Note that these are an estimate of runtimes and cannot account for overlapped jobs, schedule changes or other issues that may result in altered execution times. Results are ordered by scheduled time ascending.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The capped field in the returned &amp;lt;code&amp;gt;RuntimePreviewListing&amp;lt;/code&amp;gt; indicates that there were too many results to return (i.e. exceeded maxRecords as configured in the [[Admin Scheduler Settings|scheduler settings]] screen). If you are hitting this condition, try limiting your date range or other parameters. Due to the nature of the runtime preview, paging is not feasible.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RuntimePreviewParameters Fields&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| jobIds || N || Restricts the preview to the selected jobs.&lt;br /&gt;
|- &lt;br /&gt;
| startDate || N || Start date for the runtimes to preview (inclusive).  Defaults to the current minute.&lt;br /&gt;
|- &lt;br /&gt;
| endDate || N || End date for the runtimes to preview (inclusive). Defaults to a day after the start time. Must be after the start time.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_runtime_previews_.28supports_multiple_jobs.29|GET a list of runtime previews]]&lt;br /&gt;
&lt;br /&gt;
== Schedule a New Runtime for a Job ==&lt;br /&gt;
&lt;br /&gt;
To submit an ad hoc job run (executed immediately), or a one-time run scheduled for a later time, use  [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/RuntimeManager.html#submitRuntime(long,com.carfey.ops.api.bean.history.RuntimeSubmissionRequest,java.lang.String) RuntimeManager.submitRuntime()]. A [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/history/RuntimeSubmissionRequest.html RuntimeSubmissionRequest] must be supplied. The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/history/RuntimeSubmissionResult.html RuntimeSubmissionResult] if it succeeds, or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified job ID does not exist. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The job must be in a valid state to allow for execution (i.e. &amp;lt;code&amp;gt;UNSCHEDULED_ACTIVE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ENABLED&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;AD_HOC_ACTIVE&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RuntimeSubmissionRequest Fields&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| scheduledTime|| N || The scheduled time, when the request is for a scheduled one-time run. If not supplied, the runtime is submitted for immediate execution as an ad hoc job.&lt;br /&gt;
|- &lt;br /&gt;
| parameters || N || Zero or more parameter definitions. Parameter definitions must have values for &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;value&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; is a valid [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/ParameterType.html ParameterType]. To define multiple values for a single parameter name, simply include multiple items in the parameters collection. If the parameter name matches a parameter defined for the job, it must be of the same type, and it will completely replace all configured values at the job level.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#POST_a_new_scheduled_runtime_for_an_existing_job_.28i.e._submit_a_one-time_or_ad_hoc_run.29|POST a new scheduled runtime for an existing job]]&lt;br /&gt;
&lt;br /&gt;
== Delete a Future Scheduled Runtime for a Job ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Since Obsidian 4.7.0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
To delete a future scheduled runtime, use  [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/RuntimeManager.html#deleteOneTimeRun(long,com.carfey.ops.api.bean.history.OneTimeRunDeleteRequest,java.lang.String) RuntimeManager.deleteOneTimeRun()]. A [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/history/OneTimeRunDeleteRequest.html OneTimeRunDeleteRequest] must be supplied. The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/history/OneTimeDeletionResult.html OneTimeDeletionResult] if it succeeds, or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified job ID does not exist. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; When more than on runtime is scheduled for the given time, this function will remove gaps, that is ensure the remaining ordinals start at 0 and increment without skipping any values. For example, if you have 3 instances scheduled (ordinals 0, 1 &amp;amp; 2) and request ordinal 0 be deleted, the remaining two ordinals (1 &amp;amp; 2) will be renumbered to 0 &amp;amp; 1.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OneTimeRunDeleteRequestFields&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| scheduledTime|| Y || The future scheduled runtime.&lt;br /&gt;
|- &lt;br /&gt;
| ordinal|| Y/N || The ordinal of future dated runtime. If only one runtime is scheduled for the given date, the value can be omitted or should be 0. If more than one instance is scheduled at the given time, the actual ordinal must be specified.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#DELETE_a_future_scheduled_runtime|DELETE a future scheduled runtime]]&lt;br /&gt;
&lt;br /&gt;
== Resubmit a Failed Job Runtime ==&lt;br /&gt;
&lt;br /&gt;
If a job execution fails and you wish to resubmit it, use  [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/RuntimeManager.html#resubmitRuntime(long,java.lang.String) RuntimeManager.resubmitRuntime()] with the appropriate job runtime ID. The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/history/RuntimeResubmissionResult.html RuntimeResubmissionResult] if the action succeeds, or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified job runtime ID does not exist. &lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#POST_a_resubmission_request_for_a_failed_job_runtime|POST a resubmission request for a failed job runtime]]&lt;br /&gt;
&lt;br /&gt;
== Interrupt a Running Job Runtime ==&lt;br /&gt;
&lt;br /&gt;
To interrupt a currently running job runtime, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/RuntimeManager.html#interruptRuntime(long,java.lang.String) RuntimeManager.interruptRuntime()] with the appropriate job runtime ID. A [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/history/JobInterruptResult.html JobInterruptResult] is returned upon success, or a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] is thrown if the specified job runtime ID does not exist. &lt;br /&gt;
&lt;br /&gt;
This request can only be made once successfully. An interruption request will result in the job being terminated, as long as it does not terminate naturally very soon after the request is made, and the job is capable of shutting down. Not all jobs can be terminated. See [[Implementing_Jobs#Interruptable_Jobs|Interruptable Jobs]] for full details.&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#POST_an_interruption_request_to_kill_a_running_job|POST an interruption request to kill a running job]]&lt;br /&gt;
&lt;br /&gt;
== Post Results to a Pending (Async) Runtime  ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 4.5.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
To indicate the final status of an [[Implementing_Jobs#Async_Jobs | Async Job]], use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/RuntimeManager.html#setAsyncRuntimeResults(long,com.carfey.ops.api.bean.history.AsyncJobRuntimeResultsRequest,java.lang.String) RuntimeManager.setAsyncRuntimeResults()] with the appropriate job runtime ID. An [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/history/AsyncJobRuntimeResultsRequest.html AsyncJobRuntimeResultsRequest] is returned upon success, or a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] is thrown if the specified job runtime ID does not exist. &lt;br /&gt;
&lt;br /&gt;
This request can only be made once successfully. Final state can only be &amp;lt;code&amp;gt;COMPLETED&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;FAILED&amp;lt;/code&amp;gt; as per [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/AsyncJobRuntimeStatus.html AsyncJobRuntimeStatus]. If &amp;lt;code&amp;gt;FAILED&amp;lt;/code&amp;gt;, then a finalizationException must be provided. If &amp;lt;code&amp;gt;COMPLETED&amp;lt;/code&amp;gt;, finalizationException must be null.&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#POST_async_results|POST async results]]&lt;br /&gt;
&lt;br /&gt;
== List Job Dashboard (Latest Scheduled Runtime by Job) ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 4.10.2&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
To get a list of latest job runtimes by job(i.e. history), use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/RuntimeManager.html#listJobsDashboard(com.carfey.ops.api.bean.history.JobDashboardListingParameters) RuntimeManager.listJobsDashboard()], which accepts an optional [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/history/JobDashboardListingParameters.html JobDashboardListingParameters] instance. The results in the returned [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/history/JobDashboardListing.html JobDashboardListing] are ordered by scheduled time descending, unless overridden by the &amp;lt;code&amp;gt;sort&amp;lt;/code&amp;gt; parameter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt; field in the &amp;lt;code&amp;gt;JobDashboardListing&amp;lt;/code&amp;gt; indicates that there were too many results to return (i.e. exceeded &amp;lt;code&amp;gt;maxRecords&amp;lt;/code&amp;gt; as configured in the [[Admin Scheduler Settings|scheduler settings]] screen, or the &amp;lt;code&amp;gt;quantity&amp;lt;/code&amp;gt; parameter). To fetch the next page of results, invoke the same method with the &amp;lt;code&amp;gt;startKey&amp;lt;/code&amp;gt; field on &amp;lt;code&amp;gt;JobDashboardListingParameters&amp;lt;/code&amp;gt; set to the returned &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;JobDashboardListingParametersFields&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| jobIds || N || Restricts the search to the selected jobs. &#039;&#039;Do not combine with jobNicknames.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| jobNicknames || N || Restricts the search to the selected jobs. &#039;&#039;Do not combine with jobIds.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| hosts || N || If specified, only the latest job runtimes that are run on the specified host(s) are included. &lt;br /&gt;
|-&lt;br /&gt;
| folders|| N || If specified, only jobs matching the supplied folders are returned. If a parent path is supplied, all jobs containing that path or subpaths are included in the results. If an empty string is supplied, jobs with no folder will be returned. Supports multiple values.&lt;br /&gt;
|- &lt;br /&gt;
| startKey || N || If requesting the next page of results from a previous call, set it to the returned &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
|- &lt;br /&gt;
| quantity || N || Indicates the maximum number of results to return. This overrides the &amp;lt;code&amp;gt;maxRecords&amp;lt;/code&amp;gt; as configured in the  [[Admin Scheduler Settings|scheduler settings]] screen.&lt;br /&gt;
|- &lt;br /&gt;
| sort || N || A valid [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/SortDirection.html SortDirection], which controls the ordering of returned results. In all cases, the job scheduled time is used to sort results.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_the_latest_scheduled_runtime_by_job_.28supports_multiple_jobs.29|GET a list of the latest scheduled runtime by job]]&lt;br /&gt;
&lt;br /&gt;
= HostManager API =&lt;br /&gt;
&lt;br /&gt;
[https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/HostManager.html HostManager] is used to manage and view scheduling hosts and see additional host details including event hooks.&lt;br /&gt;
&lt;br /&gt;
== List Scheduling Hosts ==&lt;br /&gt;
&lt;br /&gt;
To get a list of known hosts which are running or recently shut down abnormally, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/HostManager.html#listHosts() HostManager.listHosts()], which returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/host/HostListing.html HostListing].&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_known_scheduling_hosts|GET a list of known scheduling hosts]]&lt;br /&gt;
&lt;br /&gt;
== Get a Specific Host ==&lt;br /&gt;
&lt;br /&gt;
To get details on a host by ID or name, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/HostManager.html#getHost(long) HostManager.getHost(id)] or [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/HostManager.html#getHost(java.lang.String) or HostManager.getHost(String)].&lt;br /&gt;
&lt;br /&gt;
The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/host/HostDetail.html HostDetail], or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified host ID or name does not exist. &lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_details_on_an_existing_scheduling_host|GET details on an existing scheduling host]]&lt;br /&gt;
&lt;br /&gt;
== Get Licence Health by Host ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 4.3.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Identical to [[Embedded_API#Get_a_Specific_Host | Get a Specific Host]], except it throws an Exception if the licence is invalid or expired.&lt;br /&gt;
&lt;br /&gt;
To get licence health on a host by ID or name, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/HostManager.html#getLicenceHealth(long) HostManager.getLicenceHealth(id)] or [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/HostManager.html#getLicenceHealth(java.lang.String) or HostManager.getLicenceHealth(String)].&lt;br /&gt;
&lt;br /&gt;
The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/host/HostDetail.html HostDetail], or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified host ID or name does not exist. &lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_licence_details_on_an_existing_scheduling_host|GET licence details on an existing scheduling host]]&lt;br /&gt;
&lt;br /&gt;
== Update a Host ==&lt;br /&gt;
&lt;br /&gt;
To enable or disable a scheduling host, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/HostManager.html#updateHost(long,com.carfey.ops.api.bean.host.HostUpdateRequest,java.lang.String) HostManager.updateHost(long)] or [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/HostManager.html#updateHost(java.lang.String,com.carfey.ops.api.bean.host.HostUpdateRequest,java.lang.String) HostManager.updateHost(String)] with a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/host/HostUpdateRequest.html HostUpdateRequest]. Both methods return a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/host/HostDetail.html HostDetail], or throw a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified host ID or name does not exist. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;HostUpdateRequest Fields&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| enabled || Y || Should this host should be enabled?&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#PUT_updates_to_an_existing_scheduling_host|PUT updates to an existing scheduling host]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== List Event Hooks ==&lt;br /&gt;
&#039;&#039;As of Obsidian 5.4.0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
To get a list of all event hooks against all hosts, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/HostManager.html#listEventHooksByHost() HostManager.listEventHooksByHost()], which returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/host/EventHookListing.html EventHookListing].&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_event_hooks|GET a list of event hooks]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Get Event Hooks on a specific Host ==&lt;br /&gt;
&#039;&#039;As of Obsidian 5.4.0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
To get event hooks on a host by ID or name, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/HostManager.html#listEventHooksByHost(long) HostManager.listEventHooksByHost(id)] or [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/HostManager.html#listEventHooksByHost(java.lang.String) or HostManager.listEventHooksByHost(String)].&lt;br /&gt;
&lt;br /&gt;
The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/host/EventHookListing.html EventHookListing], or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified host ID or name does not exist. &lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_event_hooks_on_host|GET event hooks on host]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Event Hook Resume or Pause ==&lt;br /&gt;
&#039;&#039;As of Obsidian 5.5.0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
To trigger a pause or resume of event hooks, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/HostManager.html#updateEventHook(com.carfey.ops.api.bean.host.UpdateEventHookRequest,java.lang.String) HostManager.updateEventHook(UpdateEventHookRequest,String)] which accepts an [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/host/UpdateEventHookRequest.html UpdateEventHookRequest].&lt;br /&gt;
&lt;br /&gt;
The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/host/EventHookListing.html EventHookListing], or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified host ID/name does not exist or the event hook does not exist on the specified host. &lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#POST_event_hook_pause_or_resume|POST event hook pause/resume]]&lt;br /&gt;
&lt;br /&gt;
= CustomCalendarManager API =&lt;br /&gt;
&lt;br /&gt;
The [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/CustomCalendarManager.html CustomCalendarManager] allows for managing of [[Job_Features#Custom_Calendars|Custom Calendars]].&lt;br /&gt;
&lt;br /&gt;
== List Calendars ==&lt;br /&gt;
&lt;br /&gt;
To list existing custom calendars, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/CustomCalendarManager.html#listCalendars() CustomCalendarManager.listCalendars()], which returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/calendar/CustomCalendarListing.html CustomCalendarListing].&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_custom_calendars|GET a list of custom calendars]]&lt;br /&gt;
&lt;br /&gt;
== Get Details on a Calendar ==&lt;br /&gt;
&lt;br /&gt;
To get details on an existing custom calendar, use  [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/CustomCalendarManager.html#getCalendar(long) CustomCalendarManager.getCalendar()], which returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/calendar/CustomCalendar.html CustomCalendar], or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified calendar ID does not exist.&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_details_on_an_existing_custom_calendar|GET details on an existing custom calendar]]&lt;br /&gt;
&lt;br /&gt;
== Add a Calendar ==&lt;br /&gt;
&lt;br /&gt;
To add a new custom calendar, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/CustomCalendarManager.html#addCalendar(com.carfey.ops.api.bean.calendar.CustomCalendarUpdateRequest,java.lang.String) CustomCalendarManager.addCalendar()] with a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/calendar/CustomCalendarUpdateRequest.html CustomCalendarUpdateRequest]. The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/calendar/CustomCalendar.html CustomCalendar].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CustomCalendarUpdateRequest Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| name|| Y || Calendar name&lt;br /&gt;
|- &lt;br /&gt;
| dates|| Y || Dates to exclude&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#POST_a_new_custom_calendar|POST a new custom calendar]]&lt;br /&gt;
&lt;br /&gt;
== Update a Calendar ==&lt;br /&gt;
&lt;br /&gt;
To update an existing calendar, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/CustomCalendarManager.html#updateCalendar(long,com.carfey.ops.api.bean.calendar.CustomCalendarUpdateRequest,java.lang.String) CustomCalendarManager.updateCalendar()] with a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/calendar/CustomCalendarUpdateRequest.html CustomCalendarUpdateRequest]. The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/calendar/CustomCalendar.html CustomCalendar] or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified calendar ID does not exist.&lt;br /&gt;
&lt;br /&gt;
The format of &amp;lt;code&amp;gt;CustomCalendarUpdateRequest&amp;lt;/code&amp;gt; is identical to that of [[#Add_a_Calendar|Add a Calendar]].&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#PUT_updates_to_an_existing_custom_calendar|PUT updates to an existing custom calendar]]&lt;br /&gt;
&lt;br /&gt;
== Delete a Calendar ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can delete an existing job using [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/CustomCalendarManager.html#deleteCalendar(long,java.lang.String) CustomCalendarManager.deleteCalendar()]. The method will return the final state of the calendar before deletion as a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/calendar/CustomCalendar.html CustomCalendar] or throw a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified calendar ID does not exist.&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#DELETE_an_existing_custom_calendar|DELETE an existing custom calendar]]&lt;br /&gt;
&lt;br /&gt;
= NotificationManager API =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/NotificationManager.html NotificationManager] allows for managing of Obsidian [[Event_Notifications|event notifications]], including subscribers and templates used to send notifications, and allows searching of triggered notifications.&lt;br /&gt;
&lt;br /&gt;
== List Subscribers ==&lt;br /&gt;
&lt;br /&gt;
To list existing subscribers, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/NotificationManager.html#listSubscribers() NotificationManager.listSubscribers()], which returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/SubscriberListing.html SubscriberListing].&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_subscribers|GET a list of subscribers]]&lt;br /&gt;
&lt;br /&gt;
== Get Details on a Subscriber ==&lt;br /&gt;
&lt;br /&gt;
To get details on an existing subscriber, use  [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/NotificationManager.html#getSubscriber(long) NotificationManager.getSubscriber()], which returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/Subscriber.html Subscriber], or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified subscriber ID does not exist.&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_details_of_an_existing_subscriber|GET details of an existing subscriber]]&lt;br /&gt;
&lt;br /&gt;
== Add a Subscriber ==&lt;br /&gt;
&lt;br /&gt;
To add a new subscriber, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/NotificationManager.html#addSubscriber(com.carfey.ops.api.bean.notification.SubscriberUpdateRequest,java.lang.String) NotificationManager.addSubscriber()] with a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/SubscriberUpdateRequest.html SubscriberUpdateRequest]. The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/Subscriber.html Subscriber].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SubscriberUpdateRequest Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| emailAddress|| Y || Valid email address of the subscriber. Must be unique.&lt;br /&gt;
|- &lt;br /&gt;
| active || N || Whether the subscription is active. Defaults to false.&lt;br /&gt;
|- &lt;br /&gt;
| generalSubscriptions|| N || Zero or more instances of [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/GeneralSubscriptionUpdateRequest.html GeneralSubscriptionUpdateRequest]. Each item contains a required subscription [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/Category.html Category] and [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/LogLevel.html LogLevel] in the &amp;lt;code&amp;gt;category&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;level&amp;lt;/code&amp;gt; fields respectively, an optional &amp;lt;code&amp;gt;jobId&amp;lt;/code&amp;gt; and an optional &amp;lt;code&amp;gt;active&amp;lt;/code&amp;gt; flag which defaults to false.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Only &amp;lt;code&amp;gt;ERROR&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;WARNING&amp;lt;/code&amp;gt; AND &amp;lt;code&amp;gt;INFO&amp;lt;/code&amp;gt; are valid values for &amp;lt;code&amp;gt;level&amp;lt;/code&amp;gt;. Only &amp;lt;code&amp;gt;JOB&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;JOB_CHAIN&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;JOB_CONFIG&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;JOB_RECOVERY&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LICENCE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;QUEUE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SYSTEM_PARAMETER&amp;lt;/code&amp;gt; and null are valid values for &amp;lt;code&amp;gt;category&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| jobExecutionSubscriptions || N || Zero or more instances of [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/JobExecutionSubscriptionUpdateRequest.html JobExecutionSubscriptionUpdateRequest]. Each item contains a required &amp;lt;code&amp;gt;triggerStates&amp;lt;/code&amp;gt; field which uses the [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/SubscriptionJobStatus.html SubscriptionJobStatus] enum. Note that &amp;lt;code&amp;gt;CONDITIONAL&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;COMPLETED&amp;lt;/code&amp;gt; types cannot be used on the same chain.&lt;br /&gt;
&lt;br /&gt;
If the &amp;lt;code&amp;gt;CONDITIONAL&amp;lt;/code&amp;gt; state is selected, at least one [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/JobSubscriptionCondition.html JobSubscriptionCondition] must be supplied in the &amp;lt;code&amp;gt;resultConditions&amp;lt;/code&amp;gt; field. Result conditions consist of a &amp;lt;code&amp;gt;variableName&amp;lt;/code&amp;gt;, an &amp;lt;code&amp;gt;operator&amp;lt;/code&amp;gt; using the [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/JobSubscriptionConditionOperator.html JobSubscriptionConditionOperator] enum, and for most operators, a list of &amp;lt;code&amp;gt;values&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;EXISTS&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;NOT EXISTS&amp;lt;/code&amp;gt; operators do not use values, so they must not be supplied. Operators &amp;lt;code&amp;gt;IN&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;NOT IN&amp;lt;/code&amp;gt; support one or more values, and all other operators accept a single value in the &amp;lt;code&amp;gt;values&amp;lt;/code&amp;gt; list.&lt;br /&gt;
&lt;br /&gt;
In addition, two additional optional fields control which jobs the subscription applies to. &amp;lt;code&amp;gt;jobIds&amp;lt;/code&amp;gt; is used to specify specific jobs, while &amp;lt;code&amp;gt;allJobs&amp;lt;/code&amp;gt; may be set to true to make it apply to all jobs. If &amp;lt;code&amp;gt;jobIds&amp;lt;/code&amp;gt; is supplied and &amp;lt;code&amp;gt;allJobs&amp;lt;/code&amp;gt; is set to true, &amp;lt;code&amp;gt;jobIds&amp;lt;/code&amp;gt; will be ignored.&lt;br /&gt;
&lt;br /&gt;
Finally, the &amp;lt;code&amp;gt;active&amp;lt;/code&amp;gt; flag may be supplied which defaults to true.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#POST_a_new_subscriber|POST a new subscriber]]&lt;br /&gt;
&lt;br /&gt;
== Update a Subscriber  ==&lt;br /&gt;
&lt;br /&gt;
To update an existing subscriber, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/NotificationManager.html#updateSubscriber(long,com.carfey.ops.api.bean.notification.SubscriberUpdateRequest,java.lang.String) NotificationManager.updateSubscriber()] with a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/SubscriberUpdateRequest.html SubscriberUpdateRequest]. The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/Subscriber.html Subscriber] or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified subscriber ID does not exist.&lt;br /&gt;
&lt;br /&gt;
The format of &amp;lt;code&amp;gt;SubscriberUpdateRequest&amp;lt;/code&amp;gt; is identical to that of [[#Add_a_Subscriber|Add a Subscriber]].&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#PUT_updates_to_an_existing_subscriber|PUT updates to an existing subscriber]]&lt;br /&gt;
&lt;br /&gt;
== Delete a Subscriber  ==&lt;br /&gt;
&lt;br /&gt;
You can delete an existing subscriber using [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/NotificationManager.html#deleteSubscriber(long,java.lang.String) NotificationManager.deleteSubscriber()]. The method will return the final state of the subscriber before deletion as a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/Subscriber.html Subscriber], or throw a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if it does not exist.&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#DELETE_an_existing_subscriber| DELETE an existing subscriber]]&lt;br /&gt;
&lt;br /&gt;
== List Templates ==&lt;br /&gt;
&lt;br /&gt;
To list existing notification templates, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/NotificationManager.html#listTemplates() NotificationManager.listTemplates()], which returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/TemplateListing.html TemplateListing].&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_templates|GET a list of templates]]&lt;br /&gt;
&lt;br /&gt;
== Get Details on a Template ==&lt;br /&gt;
&lt;br /&gt;
To get details on an existing template, use  [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/NotificationManager.html#getTemplate(long) NotificationManager.getTemplate()], which returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/Template.html Template], or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified template ID does not exist.&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_details_of_an_existing_template|GET details of an existing template]]&lt;br /&gt;
&lt;br /&gt;
== Add a Template ==&lt;br /&gt;
&lt;br /&gt;
To add a new template, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/NotificationManager.html#addTemplate(com.carfey.ops.api.bean.notification.TemplateUpdateRequest,java.lang.String) NotificationManager.addTemplate()] with a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/TemplateUpdateRequest.html TemplateUpdateRequest]. The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/Template.html Template].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TemplateUpdateRequest Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| name || Y || Unique name of the template.&lt;br /&gt;
|- &lt;br /&gt;
| active || N || Whether the template is active. Defaults to false.&lt;br /&gt;
|-&lt;br /&gt;
| category || N || The [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/Category.html Category] for the template, or null if it is the default generic template. Supports multiple values. Not all categories are supported. Only &amp;lt;code&amp;gt;JOB&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;JOB_CHAIN&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;JOB_CONFIG&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;JOB_RECOVERY&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LICENCE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;QUEUE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;SYSTEM_PARAMETER&amp;lt;/code&amp;gt; and null are valid values. &lt;br /&gt;
|- &lt;br /&gt;
| defaultForJobs || N || For job-related categories, setting this to true allows for a template to be used as the default template when one isn&#039;t assigned to a particular job. Defaults to false.&lt;br /&gt;
|- &lt;br /&gt;
| jobIds || N || For job-related categories, specific jobs may be assigned to use this template.&lt;br /&gt;
|- &lt;br /&gt;
| subjectTemplate || Y || A valid [[Email_Templates|Mustache template]] for the subject.&lt;br /&gt;
|- &lt;br /&gt;
| bodyTemplate || Y || A valid [[Email_Templates|Mustache template]] for the body.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#POST_a_new_template|POST a new template]]&lt;br /&gt;
&lt;br /&gt;
== Update a Template  ==&lt;br /&gt;
&lt;br /&gt;
To update an existing template, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/NotificationManager.html#updateTemplate(long,com.carfey.ops.api.bean.notification.TemplateUpdateRequest,java.lang.String) NotificationManager.updateTemplate()] with a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/TemplateUpdateRequest.html TemplateUpdateRequest]. The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/Template.html Template] or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified template ID does not exist.&lt;br /&gt;
&lt;br /&gt;
The format of &amp;lt;code&amp;gt;TemplateUpdateRequest&amp;lt;/code&amp;gt; is identical to that of [[#Add_a_Template|Add a Template]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#PUT_updates_to_an_existing_template|PUT updates to an existing template]]&lt;br /&gt;
&lt;br /&gt;
== Delete a Template  ==&lt;br /&gt;
&lt;br /&gt;
You can delete an existing template using [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/NotificationManager.html#deleteTemplate(long,java.lang.String) NotificationManager.deleteTemplate()]. The method will return the final state of the template before deletion as a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/Template.html Template], or throw a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if it does not exist.&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#DELETE_an_existing_template| DELETE an existing template]]&lt;br /&gt;
&lt;br /&gt;
== List Notifications==&lt;br /&gt;
&lt;br /&gt;
To get a list notifications, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/NotificationManager.html#listNotifications(com.carfey.ops.api.bean.notification.NotificationListingParameters) NotificationManager.listNotifications()], which accepts an optional [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/NotificationListingParameters.html NotificationListingParameters] instance. The results in the returned [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/NotificationListing.html NotificationListing] are ordered roughly according to when they were created, but ordering is not guaranteed to be in order of created time. Note that existence of records does not necessarily indicate the notification was successfully sent or received.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt; field in the &amp;lt;code&amp;gt;LogListing&amp;lt;/code&amp;gt; indicates that there were too many results to return (i.e. exceeded &amp;lt;code&amp;gt;maxRecords&amp;lt;/code&amp;gt; as configured in the  [[Admin Scheduler Settings|scheduler settings]] screen). To fetch the next page of results, invoke the same method with the &amp;lt;code&amp;gt;startKey&amp;lt;/code&amp;gt; field on &amp;lt;code&amp;gt;LogListingParameters&amp;lt;/code&amp;gt; set to the returned &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LogListingParameters Fields&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|-&lt;br /&gt;
| category || N || One or more notification log [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/Category.html Category] values used to limit the returned results.&lt;br /&gt;
|-&lt;br /&gt;
| level || N || One or more notification log [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/LogLevel.html LogLevel] values used to limit the returned results.&lt;br /&gt;
|- &lt;br /&gt;
| start || N || Start date for the notifications to return (inclusive).  Defaults to the current minute.&lt;br /&gt;
|- &lt;br /&gt;
| end || N || Start date for the notifications to return (inclusive). Defaults to a day after the start time. Must be after the start time.&lt;br /&gt;
|- &lt;br /&gt;
| startKey || N || If requesting the next page of results from a previous call, set it to the returned &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_notifications|GET a list of notifications]]&lt;br /&gt;
&lt;br /&gt;
== Get Details on a Notification ==&lt;br /&gt;
&lt;br /&gt;
To get details on a single notification entry use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/NotificationManager.html#getNotification(long) NotificationManager.getNotification()]. The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/notification/Notification.html Notification] or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if it does not exist. Note that existence of a record does not necessarily indicate the notification was successfully sent or received.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_details_of_an_existing_notification|GET details of an existing notification]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= LogManager API =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/LogManager.html LogManager] is used to retrieve event log entries.&lt;br /&gt;
&lt;br /&gt;
== List Log Entries==&lt;br /&gt;
&lt;br /&gt;
To get a list log entries, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/LogManager.html#listLogs(com.carfey.ops.api.bean.log.LogListingParameters) LogManager.listLogs()], which accepts an optional [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/log/LogListingParameters.html LogListingParameters] instance. The results in the returned [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/log/LogListing.html LogListing] are ordered roughly according to when they were created, but ordering is not guaranteed to be in order of created time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt; field in the &amp;lt;code&amp;gt;LogListing&amp;lt;/code&amp;gt; indicates that there were too many results to return (i.e. exceeded &amp;lt;code&amp;gt;maxRecords&amp;lt;/code&amp;gt; as configured in the  [[Admin Scheduler Settings|scheduler settings]] screen). To fetch the next page of results, invoke the same method with the &amp;lt;code&amp;gt;startKey&amp;lt;/code&amp;gt; field on &amp;lt;code&amp;gt;LogListingParameters&amp;lt;/code&amp;gt; set to the returned &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LogListingParameters Fields&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|-&lt;br /&gt;
| host || N || If specified, only logs from the specified host name(s) are included. Supports multiple values.&lt;br /&gt;
|-&lt;br /&gt;
| filterText || N || If specified, only messages containing the supplied filter text are returned. &#039;%&#039; can be used as a wildcard.&lt;br /&gt;
|-&lt;br /&gt;
| category || N || One or more log [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/Category.html Category] values used to limit the returned results.&lt;br /&gt;
|-&lt;br /&gt;
| level || N || One or more log [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/LogLevel.html LogLevel] values used to limit the returned results.&lt;br /&gt;
|- &lt;br /&gt;
| start || N || Start date for the logs to return (inclusive).  Defaults to the current minute.&lt;br /&gt;
|- &lt;br /&gt;
| end || N || Start date for the logs to return (inclusive). Defaults to a day after the start time. Must be after the start time.&lt;br /&gt;
|- &lt;br /&gt;
| startKey || N || If requesting the next page of results from a previous call, set it to the returned &amp;lt;code&amp;gt;nextPageStartKey&amp;lt;/code&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_logs|GET a list of logs]]&lt;br /&gt;
&lt;br /&gt;
== Get Details on a Log Entry ==&lt;br /&gt;
&lt;br /&gt;
To get details on a single log entry use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/LogManager.html#getLog(long) LogManager.getLog()]. The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/log/Log.html Log] or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if it does not exist. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_details_of_an_existing_log|GET details of an existing log]]&lt;br /&gt;
&lt;br /&gt;
= SystemParameterManager API =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/SystemParameterManager.html SystemParameterManager] allows for listing and updating Obsidian [[Admin_Scheduler Settings|scheduler settings]].&lt;br /&gt;
&lt;br /&gt;
== List System Parameters ==&lt;br /&gt;
&lt;br /&gt;
To list editable system parameters, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/SystemParameterManager.html#listParameters() SystemParameterManager.listParameters()], which returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/system/SystemParameterListing.html SystemParameterListing].&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_system_parameters|GET a list of system parameters]]&lt;br /&gt;
&lt;br /&gt;
== Get Details on a System Parameter ==&lt;br /&gt;
&lt;br /&gt;
To get details on specific system parameter, use  [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/SystemParameterManager.html#getParameter(java.lang.String) SystemParameterManager.getParameter()], which returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/system/SystemParameter.html SystemParameter], or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified system parameter does not exist.&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_details_of_a_system_parameter|GET details of system parameter]]&lt;br /&gt;
&lt;br /&gt;
== Update a System Parameter  ==&lt;br /&gt;
&lt;br /&gt;
To update a system parameter&#039;s value, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/SystemParameterManager.html#updateParameter(java.lang.String,java.lang.String,java.lang.String) SystemParameterManager.updateParameter()] with a value supplied as a String. The supplied value must be a valid String representation of the type for the system parameter (e.g. Integer, Boolean).&lt;br /&gt;
&lt;br /&gt;
The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/system/SystemParameter.html SystemParameter] or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified system parameter does not exist.&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#PUT_updates_to_a_system_parameter|PUT updates to a system parameter]]&lt;br /&gt;
&lt;br /&gt;
= UserManager API =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/UserManager.html UserManager] allows for managing of Obsidian [[Admin_User_Management|users]] when native authentication is used. This API cannot be used when using LDAP or other custom authentication methods.&lt;br /&gt;
&lt;br /&gt;
== List Users ==&lt;br /&gt;
&lt;br /&gt;
To list existing users, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/UserManager.html#listUsers() UserManager.listUsers()], which returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/user/UserListing.html UserListing].&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_users|GET a list of users]]&lt;br /&gt;
&lt;br /&gt;
== Get Details on a User ==&lt;br /&gt;
&lt;br /&gt;
To get details on an existing user, use  [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/UserManager.html#getUser(long) UserManager.getUser()], which returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/user/User.html User], or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified user ID does not exist.&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_details_of_an_existing_user|GET details of an existing user]]&lt;br /&gt;
&lt;br /&gt;
== Add a User ==&lt;br /&gt;
&lt;br /&gt;
To add a new user, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/UserManager.html#addUser(com.carfey.ops.api.bean.user.UserCreationRequest,java.lang.String) UserManager.addUser()] with a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/user/UserCreationRequest.html UserCreationRequest]. The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/user/User.html User].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UserCreationRequest Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| userName || Y || Unique user name used to log in.&lt;br /&gt;
|- &lt;br /&gt;
| active || N || Whether the user is enabled. Defaults to false.&lt;br /&gt;
|- &lt;br /&gt;
| roles || N ||Zero or more [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/Role.html Role] values.  Supplying no roles indicates it is a normal read-only user.&lt;br /&gt;
|- &lt;br /&gt;
| password || Y || A password at least 6 characters long.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#POST_a_new_user|POST a new user]]&lt;br /&gt;
&lt;br /&gt;
== Update a User  ==&lt;br /&gt;
&lt;br /&gt;
To update an existing user, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/UserManager.html#updateUser(long,com.carfey.ops.api.bean.user.UserUpdateRequest,java.lang.String) UserManager.updateUser()] with a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/user/UserUpdateRequest.html UserUpdateRequest]. The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/user/User.html User] or throws a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if the specified user ID does not exist.&lt;br /&gt;
&lt;br /&gt;
Note that only supplied fields are updated. If a field is left null, the existing value will be left unchanged.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UserUpdateRequest Format&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|- &lt;br /&gt;
| active || N || Whether the user is enabled. Defaults to the existing active state.&lt;br /&gt;
|- &lt;br /&gt;
| roles || N || Zero or more [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/enums/Role.html Role] values. Supplying an empty list of roles indicates it is a normal read-only user. Supplying null indicates that the roles should not be updated.&lt;br /&gt;
|- &lt;br /&gt;
| password || N || A password at least 6 characters long. If not supplied, the password is not changed.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#PUT_updates_to_an_existing_user|PUT updates to an existing user]]&lt;br /&gt;
&lt;br /&gt;
== Delete a User  ==&lt;br /&gt;
&lt;br /&gt;
You can delete an existing user using [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/UserManager.html#deleteUser(long,java.lang.String) UserManager.deleteUser()]. The method will return the final state of the user before deletion as a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/user/User.html User], or throw a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/MissingEntityException.html MissingEntityException] if it does not exist.&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#DELETE_an_existing_user| DELETE an existing user]]&lt;br /&gt;
&lt;br /&gt;
== List Known MFA Users  ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;As of Obsidian 5.0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
To list all known MFA users, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/UserManager.html#listMfaUsers() UserManager.listMfaUsers()], which returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/user/MfaUserListing.html MfaUserListing].&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_known_MFA_users|GET a list of known MFA users]]&lt;br /&gt;
&lt;br /&gt;
== Reset Users&#039; MFA State ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;As of Obsidian 5.0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
You can reset one or more users&#039; MFA state using [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/UserManager.html#resetMfa(com.carfey.ops.api.bean.user.ResetMfaRequest,java.lang.String) UserManager.resetMfa()] with a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/user/ResetMfaRequest.html ResetMfaRequest]. The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/user/MfaUserListing.html MfaUserListing] which echoes the user names that were reset.&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#PUT_MFA_user_resets| PUT MFA user resets]]&lt;br /&gt;
&lt;br /&gt;
= SystemRestore API =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/SystemRestoreManager.html SystemRestoreManager] allows for importing and exporting full Obsidian configuration. See [[Initializing_and_Restoring|Initializing and Restoring]] for more details.&lt;br /&gt;
&lt;br /&gt;
== Get System Restore Configuration ==&lt;br /&gt;
&lt;br /&gt;
To extract the current system restore configuration, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/SystemRestoreManager.html#getConfiguration() SystemRestoreManager.getConfiguration()] which accepts an optional [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/system/restore/SystemRestoreParameters.html SystemRestoreParameters] and returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/system/restore/SystemRestoreConfiguration.html SystemRestoreConfiguration].&lt;br /&gt;
&lt;br /&gt;
You can limit the returned configuration by providing a SystemRestoreParameters.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SystemRestoreParameters Fields&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field || Required? || Notes&lt;br /&gt;
|-&lt;br /&gt;
| excludeItems || N || Allows filtering out of specific groups of system configuration items. Supported values are &amp;lt;code&amp;gt;jobs&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;chains&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;conflicts&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;systemParameters&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;customCalendars&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;globalParameters&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;users&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;templates&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;subscribers&amp;lt;/code&amp;gt;. Filtering out jobs automatically also filters out chains and conflicts and drops any other job references (such as in templates and subscribers).&lt;br /&gt;
|-&lt;br /&gt;
| jobNicknames || N || Allows targeting only specific jobs in the jobs export. Filtering out any jobs automatically filters out all conflicts, any chains where these jobs exist and and drops any other references to these jobs (such as in templates and subscribers).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_system_restore_configuration|GET a system restore configuration]]&lt;br /&gt;
&lt;br /&gt;
== Update System Restore Configuration ==&lt;br /&gt;
&lt;br /&gt;
To import a system restore configuration (partial or complete), use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/SystemRestoreManager.html#updateConfiguration(com.carfey.ops.api.bean.system.restore.SystemRestoreConfiguration,java.lang.String) SystemRestoreManager.updateConfiguration()] with a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/system/restore/SystemRestoreConfiguration.html SystemRestoreConfiguration]. The method returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/system/restore/SystemRestoreConfiguration.html SystemRestoreConfiguration] representing the full system state, which may not be the same as the input value.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#PUT_a_system_restore_configuration|PUT a system restore configuration]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= ScheduleAliasManager API =&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;As of Obsidian 5.0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/ScheduleAliasManager.html ScheduleAliasManager] allows for retrieving, creating, editing and deleting schedule aliases. See [[Admin_Schedule_Aliases|Schedule Aliases]] for more information.&lt;br /&gt;
&lt;br /&gt;
== List Schedule Aliases ==&lt;br /&gt;
&lt;br /&gt;
To list the current schedule aliases, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/ScheduleAliasManager.html#listScheduleAliases() ScheduleAliasManager.listScheduleAliases()] which returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/schedule/ScheduleAliasListing.html ScheduleAliasListing].&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_schedule_aliases|GET a list of schedule aliases]]&lt;br /&gt;
&lt;br /&gt;
== Create or Update Schedule Alias ==&lt;br /&gt;
&lt;br /&gt;
To create or update a schedule alias, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/ScheduleAliasManager.html#addOrUpdateScheduleAlias(java.lang.String,java.lang.String,java.lang.String) ScheduleAliasManager.addOrUpdateScheduleAlias()]. The method returns the updated [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/schedule/ScheduleAlias.html ScheduleAlias] representing the full system state, which may not be the same as the input value.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#PUT_a_schedule_alias|PUT a schedule alias]]&lt;br /&gt;
&lt;br /&gt;
== Get a Schedule Alias ==&lt;br /&gt;
&lt;br /&gt;
To retrieve a current schedule alias, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/ScheduleAliasManager.html#getScheduleAlias(java.lang.String) ScheduleAliasManager.getScheduleAlias()] which returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/schedule/ScheduleAlias.html ScheduleAlias].&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_schedule_alias|GET a schedule alias]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Delete a Schedule Alias ==&lt;br /&gt;
&lt;br /&gt;
To delete an unused schedule alias, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/ScheduleAliasManager.html#deleteScheduleAlias(java.lang.String,java.lang.String) ScheduleAliasManager.deleteScheduleAlias()] which returns the deleted [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/schedule/ScheduleAlias.html ScheduleAlias].&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#DELETE_a_schedule_alias|DELETE a schedule alias]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= StatsManager API =&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;As of Obsidian 6.4.0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/StatsManager.html StatsManager] allows for retrieving job execution statistics. Assumes the [[Built-in_Jobs#Obsidian_Execution_Statistics_Job|Execution Statistics Job]] has run at least once successfully.&lt;br /&gt;
&lt;br /&gt;
== List Stats ==&lt;br /&gt;
&lt;br /&gt;
To list the job execution statistics, use [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/embedded/StatsManager.html#listStats(com.carfey.ops.api.bean.stats.StatsListingRequest) StatsManager.listStats()] which returns a [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/api/bean/stats/StatsListing.html StatsListing].&lt;br /&gt;
&lt;br /&gt;
REST equivalent: [[REST_Endpoints#GET_a_list_of_job_execution_statistics|GET a list of job execution statistics]]&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Email_Templates&amp;diff=4144</id>
		<title>Email Templates</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Email_Templates&amp;diff=4144"/>
		<updated>2026-07-31T14:49:50Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian supports customizable templates for all email notifications.&lt;br /&gt;
&lt;br /&gt;
If you have questions or require help building your own templates, please [[Contact Carfey Software|contact us]] for free support.&lt;br /&gt;
&lt;br /&gt;
== Template Basics &amp;amp; Language ==&lt;br /&gt;
&lt;br /&gt;
Obsidian comes with two basic email templates that are suitable for most users. For those that wish to have more control, templates can be configured in the &#039;&#039;&#039;[[Admin Templates|Email Templates]]&#039;&#039;&#039; screen.&lt;br /&gt;
&lt;br /&gt;
Both subject and body fields can be customized through  templates. Note that body templates are used for &#039;&#039;&#039;HTML only&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Obsidian email templates use [http://mustache.github.io/ Mustache], which is a logic-less templating language commonly used in various applications. Specifically, Obsidian uses [https://github.com/samskivert/jmustache jMustache], and it can be referred to when looking for feature support and general syntax in your templates.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Obsidian cannot guarantee that configured templates are valid. You are responsible for ensuring the correctness or your templates according to the information provided below. If you have configured Obsidian to use invalid templates that fail, Obsidian will fall back to internal templates so that notifications are not lost.&lt;br /&gt;
&lt;br /&gt;
== Templating Variables ==&lt;br /&gt;
&lt;br /&gt;
The following table documents all data elements that are available in email templates. Note that some data elements are only available in certain contexts, so your templates should be written accordingly. The &#039;&#039;Available Scope&#039;&#039; column indicates which types of events the variables are used in. For example, some variables are only present for job-related event categories, and some are only present for Job Run events.&lt;br /&gt;
&lt;br /&gt;
If you wish to see a sample template, simply navigate to the &#039;&#039;&#039;[[Admin Templates|Email Templates]]&#039;&#039;&#039; screen in the admin UI, and take a look at the default templates that are pre-installed.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Variable Name || Type || Available Scope || Notes&lt;br /&gt;
|-&lt;br /&gt;
| subject || Text || Always || This the basic event summary message.&lt;br /&gt;
|-&lt;br /&gt;
| detailItems || Text (multiple) || Always || The list of all detail messages provided with the event. For example, when a job&#039;s configuration is updated, a separate item for each change will be represented here. For job execution failures, this will contain the stack trace. In some cases, this may be empty.&lt;br /&gt;
|-&lt;br /&gt;
| detail || Text || Always || This the combined form of &amp;lt;code&amp;gt;detailItems&amp;lt;/code&amp;gt;. In some cases, this may be empty.&lt;br /&gt;
|-&lt;br /&gt;
| hostName || Text || Always || The host name / designator that originated the event.&lt;br /&gt;
|-&lt;br /&gt;
| hostUrl || Text || Always || The host URL as configured in the [[Admin Scheduler Settings|scheduler settings]] screen. This can be used to link to the Obsidian installation in emails.&lt;br /&gt;
|-&lt;br /&gt;
| hasDetail || Boolean || Always || Indicates if any &amp;lt;code&amp;gt;detailItems&#039;&amp;lt;/code&amp;gt; exist. Can be used for conditionals.&lt;br /&gt;
|-&lt;br /&gt;
| level || Text || Always || Indicates the level of the event. One of: &amp;lt;code&amp;gt;INFO&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;WARNING&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ERROR&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;FATAL&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| category || Text || Always || Indicates the category of the event. One of: &amp;lt;code&amp;gt;SYSTEM_PARAMETER&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;QUEUE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;JOB&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;JOB_CHAIN&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;JOB_CONFIG&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;JOB_RUN&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;JOB_RECOVERY&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LICENCE&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| levelDisplayName || Text || Always || Indicates the level of the event, in a pretty format. One of: &amp;lt;code&amp;gt;Info&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Warning&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Error&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Fatal&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| categoryDisplayName || Text || Always || Indicates the category of the event, in a pretty format. One of: &amp;lt;code&amp;gt;System Parameter&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Queue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Job&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Job Chain&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Job Config&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Job Run&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Job Recovery&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Licence&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| recipients || Text (multiple) || Always || The email addresses of all recipients of this message.&lt;br /&gt;
|-&lt;br /&gt;
| fatal || Boolean || Always || Indicates if the level was &amp;lt;code&amp;gt;FATAL&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| error || Boolean || Always || Indicates if the level was &amp;lt;code&amp;gt;ERROR&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| errorOrHigher || Boolean || Always || Indicates if the level was &amp;lt;code&amp;gt;FATAL&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ERROR&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| warn || Boolean || Always || Indicates if the level was &amp;lt;code&amp;gt;WARNING&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| warnOrHigher || Boolean || Always || Indicates if the level was &amp;lt;code&amp;gt;FATAL&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ERROR&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;WARNING&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| info || Boolean || Always || Indicates if the level was &amp;lt;code&amp;gt;INFO&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| job || Complex Object || Job Events only || For any job-related category, this object provides the basic job configuration. Attributes that can be accessed on the job are: &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;jobClass&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;minExecutionDuration&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;maxExecutionDuration&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;nickname&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;pickupBufferMinutes&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;recoveryType&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| jobState || Complex Object || Job Events only || For any job-related category, this object provides the current job schedule configuration, or for Job Run events, the schedule applicable to that job history record. Attributes that can be accessed on the jobState are: &amp;lt;code&amp;gt;effectiveDate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;endDate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;jobStatus&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;schedule&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| currentSchedule || Text || Job Events only || This is a simplified way to access &amp;lt;code&amp;gt;jobState.schedule&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| jobHistory || Complex Object || Job Run Events only || For any Job run events (i.e.. completion or failure), this object contains information on the job execution (aka history). Attributes that can be accessed on the job are: &amp;lt;code&amp;gt;adHoc&amp;lt;/code&amp;gt; (boolean), &amp;lt;code&amp;gt;resubmission&amp;lt;/code&amp;gt; (boolean), &amp;lt;code&amp;gt;runningHost&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;scheduledTime&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;submissionMode&amp;lt;/code&amp;gt;. The field &amp;lt;code&amp;gt;chainedFrom&amp;lt;/code&amp;gt; provides access to the source chain job history when applicable, which has the same format as this &amp;lt;code&amp;gt;jobHistory&amp;lt;/code&amp;gt; object. From either this object or &amp;lt;code&amp;gt;chainedFrom&amp;lt;/code&amp;gt;, the &amp;lt;code&amp;gt;job&amp;lt;/code&amp;gt; field provides access to the job configuration in the same format as the &amp;lt;code&amp;gt;job&amp;lt;/code&amp;gt; element described above. &lt;br /&gt;
|-&lt;br /&gt;
| jobResults || List of Objects || Job Run Events only || This field contains saved job results as a list of objects with two attributes: &amp;lt;code&amp;gt;key&amp;lt;/code&amp;gt; for the name and &amp;lt;code&amp;gt;value&amp;lt;/code&amp;gt; for a list of the applicable values. Example: iterate over all results and output the values separated by commas: &amp;lt;pre&amp;gt;{{#jobResults}}{{^-first}}; {{/-first}}{{key}}={{#value}}{{^-first}}, {{/-first}}{{.}}{{/value}}{{/jobResults}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| jobResultMap || Map Object || Job Run Events only || This is an alternate format of &amp;lt;code&amp;gt;jobResults&amp;lt;/code&amp;gt; stored in a map. This allows you to easily access a specific result by name. Example: &amp;lt;pre&amp;gt;{{#jobResultMap.outputFiles}}{{.}}{{/jobResultMap.outputFiles}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| jobParameters || Complex Object || Job Events only || This field contains saved job parameters as a list of objects with two attributes: &amp;lt;code&amp;gt;key&amp;lt;/code&amp;gt; for the name and &amp;lt;code&amp;gt;value&amp;lt;/code&amp;gt; for a list of the applicable values . Example: iterate over all results and output the values separated by commas: &amp;lt;pre&amp;gt;{{#jobParameters}}{{^-first}}; {{/-first}}{{key}}={{#value}}{{^-first}}, {{/-first}}{{.}}{{/value}}{{/jobParameters}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| jobParameterMap || List of Objects|| Job Events only ||  This is an alternate format of &amp;lt;code&amp;gt;jobParameters&amp;lt;/code&amp;gt; stored in a map. This allows you to easily access a specific parameter by name. Example: &amp;lt;pre&amp;gt;{{#jobParameterMap.archiveDirectory}}{{.}}{{/jobParameterMap.archiveDirectory}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Sample Syntax ==&lt;br /&gt;
&lt;br /&gt;
The following shows various ways data can be accessed from within an Obsidian email template. This example lacks HTML formatting and is meant to show how to access and display different variables available to your templates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Current Job Schedule: {{currentSchedule}}&lt;br /&gt;
Detail: {{detail}}&lt;br /&gt;
{{#detailItems}}&lt;br /&gt;
Detail {{-index}}: {{.}}&lt;br /&gt;
{{/detailItems}}&lt;br /&gt;
Error: {{#error}}true{{/error}}{{^error}}false{{/error}}&lt;br /&gt;
Error or Higher: {{#errorOrHigher}}true{{/errorOrHigher}}{{^errorOrHigher}}false{{/errorOrHigher}}&lt;br /&gt;
Fatal: {{#fatal}}true{{/fatal}}{{^fatal}}false{{/fatal}}&lt;br /&gt;
Warn: {{#warn}}true{{/warn}}{{^warn}}false{{/warn}}&lt;br /&gt;
Warn or Higher: {{#warnOrHigher}}true{{/warnOrHigher}}{{^warnOrHigher}}false{{/warnOrHigher}}&lt;br /&gt;
Info: {{#info}}true{{/info}}{{^info}}false{{/info}}&lt;br /&gt;
Host Name: {{hostName}}&lt;br /&gt;
Host Url: {{hostUrl}}&lt;br /&gt;
Level: {{level}} - {{levelDisplayName}}&lt;br /&gt;
Category: {{category}} - {{categoryDisplayName}}&lt;br /&gt;
Recipients: {{#recipients}}{{^-first}}, {{/-first}}{{.}}{{/recipients}}&lt;br /&gt;
Subject: {{subject}}&lt;br /&gt;
Job: {{#job}}{{id}} {{nickname}} {{pickupBufferMinutes}} {{recoveryType}} {{jobClass}} {{minExecutionDuration}} {{maxExecutionDuration}}{{/job}}&lt;br /&gt;
Job State: {{#jobState}}{{effectiveDate}}-{{endDate}} {{jobStatus}} {{schedule}}{{/jobState}}&lt;br /&gt;
Job History: {{#jobHistory}}{{adHoc}} {{resubmission}} {{scheduledTime}} {{submissionMode}} {{runningHost}}{{#chainedFrom}}; Chained from: {{this.job.nickname}} ({{this.scheduledTime}}){{/chainedFrom}}{{/jobHistory}}&lt;br /&gt;
Job Results: {{#jobResults}}{{^-first}}; {{/-first}}{{key}}={{#value}}{{^-first}}, {{/-first}}{{.}}{{/value}}{{/jobResults}}&lt;br /&gt;
Job Results by Name: {{#jobResultMap}}{{#result1}}{{^-first}}, {{/-first}}{{.}}{{/result1}}{{/jobResultMap}}&lt;br /&gt;
Job Results by Name (short): {{#jobResultMap.result2}}{{.}}{{/jobResultMap.result2}}&lt;br /&gt;
Job Parameters: {{#jobParameters}}{{^-first}}; {{/-first}}{{key}}={{#value}}{{^-first}}, {{/-first}}{{.}}{{/value}}{{/jobParameters}}&lt;br /&gt;
Job Parameters by Name: {{#jobParameterMap}}{{#param1}}{{^-first}}, {{/-first}}{{.}}{{/param1}}{{/jobParameterMap}}&lt;br /&gt;
Job Parameters by Name (short): {{#jobParameterMap.param2}}{{.}}{{/jobParameterMap.param2}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Cron&amp;diff=4143</id>
		<title>Cron</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Cron&amp;diff=4143"/>
		<updated>2026-07-31T14:49:49Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Job schedules in Obsidian are specified using [http://en.wikipedia.org/wiki/cron cron] syntax, with some custom extensions to support [[#Recurrence|recurrence]]. This page provides a reference for writing your own cron expressions, and documents the cron extensions that Obsidian supports. &lt;br /&gt;
&lt;br /&gt;
= Basic Format =&lt;br /&gt;
&lt;br /&gt;
Cron expressions are written using 5 fields separated by spaces, as indicated below. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.------------------------- minute (0 - 59)&lt;br /&gt;
|   .--------------------- hour (0 - 23)&lt;br /&gt;
|   |   .----------------- day of month (1 - 31)&lt;br /&gt;
|   |   |   .------------- month (1 - 12)&lt;br /&gt;
|   |   |   |   .--------- day of week (0 - 7) (Sunday = 0 or 7)&lt;br /&gt;
|   |   |   |   |   .----- year (optional)&lt;br /&gt;
|   |   |   |   |   |   .- TimeZone ID (optional)  * available as of Obsidian 4.2.0&lt;br /&gt;
|   |   |   |   |   |   |&lt;br /&gt;
*   *   *   *   *   *   *&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ordinals indicate specific times, days or months that apply, while an asterisk (*) means there is no restriction on that field. For example, using an asterisk for the month field means the schedule runs during all months, while using &amp;quot;1&amp;quot; would mean it only runs in January. Using 5 asterisks will cause a job to run every minute.&lt;br /&gt;
&lt;br /&gt;
The most basic cron expressions use simple ordinals or an asterisk for each field. As an example, the pattern &amp;lt;code&amp;gt;30 19 * * 5&amp;lt;/code&amp;gt; means 7:30 PM on all Fridays.&lt;br /&gt;
&lt;br /&gt;
Multiple Cron patterns may be specified for a single schedule by delimiting them with a semi-colon (e.g. &amp;lt;code&amp;gt;35 8 * * * *;20 12 * * *;40 16 * * *&amp;lt;/code&amp;gt;). This means that the job will fire if it matches any of the supplied patterns.&lt;br /&gt;
&lt;br /&gt;
As of &#039;&#039;&#039;Obsidian 4.2.0&#039;&#039;&#039;, &#039;&#039;&#039;TimeZone ID&#039;&#039;&#039; support was added. This allows for a cron pattern to be associated with a specific TimeZone. The job will fire according to the timing of the TimeZone specified regardless of the default TimeZone of the Obsidian cluster. Supported TimeZone IDs can be found by running  [https://docs.oracle.com/javase/7/docs/api/java/util/TimeZone.html#getAvailableIDs() TimeZone.getAvailableIDs()] in your runtime JVM.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Special Characters and Allowed Values =&lt;br /&gt;
&lt;br /&gt;
In addition to ordinals and asterisks, Obsidian supports special characters to support clearer or more complex expressions, as indicated below. These characters allow for ranges, lists and other special handling.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Field Name || Allowed Ordinal Values || Allowed Special Characters || Required&lt;br /&gt;
|-&lt;br /&gt;
| Minute || 0-59 || * / , - || Yes&lt;br /&gt;
|-&lt;br /&gt;
| Hour || 0-23 || * / , - || Yes&lt;br /&gt;
|-&lt;br /&gt;
| Day of Month || 1-31 || * / , - L W LW &amp;lt; &amp;gt; ≥ ≤ ~ SUN-SAT || Yes&lt;br /&gt;
|-&lt;br /&gt;
| Month || 1-12 or JAN-DEC || * / , - || Yes&lt;br /&gt;
|-&lt;br /&gt;
| Day of Week || 0-7 or SUN-SAT || * / , - L # || Yes&lt;br /&gt;
|-&lt;br /&gt;
| Year || 2010-2999 || * / , - || No&lt;br /&gt;
|-&lt;br /&gt;
| TimeZone ID || [https://docs.oracle.com/javase/7/docs/api/java/util/TimeZone.html#getAvailableIDs() TimeZone.getAvailableIDs()] || None  || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Special Character Usage ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Special Character || Examples || Description&lt;br /&gt;
|-&lt;br /&gt;
| Asterisk (*) || &amp;lt;code&amp;gt;0 0 * * *&amp;lt;/code&amp;gt;&amp;lt;br/&amp;gt;(run every midnight) || Asterisks indicate that the expression will match for all values of the field. For example, using an asterisk in the 4th field (month) would indicate every month.&lt;br /&gt;
|-&lt;br /&gt;
| Forward slash (/) || &amp;lt;code&amp;gt;0/2 * * * *&amp;lt;/code&amp;gt;&amp;lt;br/&amp;gt;(every even minute) || Forward slashes are used to describe increments. For example,  0/15 or */15 in the 1st field (minutes) would indicate the 0th minute of the hour and every 15 minutes thereafter. The value before the slash indicates the start value, so 1/2 for the 1st field (minutes) would indicate every odd minute.&lt;br /&gt;
|-&lt;br /&gt;
| Comma (,) || &amp;lt;code&amp;gt;0/5,7 * * * *&amp;lt;/code&amp;gt;&amp;lt;br/&amp;gt;(every 5 minutes, and at 7th minute) || Commas are used to separate items of a list. For example, using &amp;quot;MON,WED,FRI&amp;quot; in the 5th field (day-of-week) would mean Mondays, Wednesdays and Fridays. Commas can also be used to combine ranges and increments. &lt;br /&gt;
|-&lt;br /&gt;
| Hyphen (-) || &amp;lt;code&amp;gt;0-15 * * * *&amp;lt;/code&amp;gt;&amp;lt;br/&amp;gt;(run from 0th to 15th minute inclusive) || Hyphens are used to define ranges. For example, 1-3 in the 4th field (month) would indicate January through March inclusive. This could also be written as JAN-MAR.&lt;br /&gt;
|-&lt;br /&gt;
| Hyphen (-) with forward slash (/) || &amp;lt;code&amp;gt;0 8-20/3 * * * *&amp;lt;/code&amp;gt;&amp;lt;br/&amp;gt;(every 3rd hour from 8AM and 10PM) || Forward slashes are used to describe increments. The hyphen provides the boundaries for the increments. For example, 1-15/3 would be the equivalent of 1,4,7,10,13.&lt;br /&gt;
|-&lt;br /&gt;
| L || &amp;lt;code&amp;gt;0 0 * * 5L&amp;lt;/code&amp;gt;&amp;lt;br/&amp;gt;(midnight on last Friday of the month) || &amp;quot;L&amp;quot; stands for last, and can be used in the day-of-week or day-of-month fields. For example, &amp;quot;5L&amp;quot; in the day-of-week field would mean the last Friday of a given month. In the day-of-month field, &amp;quot;L&amp;quot; is used alone to specify the last day of the month.&lt;br /&gt;
|-&lt;br /&gt;
| W || &amp;lt;code&amp;gt;0 0 15W * *&amp;lt;/code&amp;gt;&amp;lt;br/&amp;gt; (midnight on weekday closest to the 15th of the month) || &amp;quot;W&amp;quot; is allowed for the day-of-month field. This is used to specify the weekday nearest the given ordinal. For example,specifying &amp;quot;15W&amp;quot; for the day-of-month field, the meaning is &amp;quot;the nearest weekday to the 15th of the month&amp;quot;. If the 15th is a Saturday, the trigger will fire on Friday the 14th. If the 15th is a Sunday, the trigger will fire on Monday the 16th. If the 15th is a Tuesday, then it will fire on Tuesday the 15th. However if you specify &amp;quot;1W&amp;quot; as the value for day-of-month, and the 1st is a Saturday, the trigger will fire on Monday the 3rd, as it will not jump over the boundary of a month&#039;s days. The &amp;quot;W&amp;quot; character can only be specified when the day-of-month is a single day, not a range or list of days, or in reference to a proximity expression ( ≥ ≤ &amp;lt; &amp;gt; )&lt;br /&gt;
|-&lt;br /&gt;
| LW || &amp;lt;code&amp;gt;0 0 LW * *&amp;lt;/code&amp;gt;&amp;lt;br/&amp;gt; (midnight on weekday closest to the last day of the month) || &amp;quot;LW&amp;quot; is allowed for the day-of-month field. This is used to specify the weekday nearest the last day of the month. The &amp;quot;LW&amp;quot; instruction can only be specified when it is the only day-of-month value. Cannot be combined with a day-of-week value.&#039;&#039;&#039;As of Obsidian 4.10.5&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Hash (#) || &amp;lt;code&amp;gt;0 0 * * 5#2&amp;lt;/code&amp;gt;&amp;lt;br/&amp;gt;(midnight on 2nd Friday of month) || The hash character is allowed for the day-of-week field, and must be followed by a number between one and five. It allows you to specify constructs such as &amp;quot;the second Friday&amp;quot; (5#2) of a given month.&lt;br /&gt;
|-&lt;br /&gt;
| Greater Than (&amp;gt;) || &amp;lt;code&amp;gt;0 0 TUE&amp;gt;10 * * *&amp;lt;/code&amp;gt;&amp;lt;br/&amp;gt;(midnight on the first Tuesday after the 10th) || The greater than character is allowed for the day-of-month field, must be preceded by a W or Day of Week expression (SUN-SAT) and followed by a day of month ordinal. It allows you to specify constructs such as &amp;quot;the first Tuesday after the 10th&amp;quot; or &amp;quot;the first weekday after the 20th&amp;quot;. &#039;&#039;&#039;As of Obsidian 6.2.0.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Less Than (&amp;lt;) || &amp;lt;code&amp;gt;0 0 SAT&amp;lt;15 * * *&amp;lt;/code&amp;gt;&amp;lt;br/&amp;gt;(midnight on the first Saturday before the 15th) || The less than character is allowed for the day-of-month field, must be preceded by a W or Day of Week expression (SUN-SAT) and followed by a day of month ordinal. It allows you to specify constructs such as &amp;quot;the first Saturday before the 15th&amp;quot; or &amp;quot;the first weekday before the 5th&amp;quot;. &#039;&#039;&#039;As of Obsidian 6.2.0.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Less Than Or Equal To (≤) || &amp;lt;code&amp;gt;0 0 W≤20 * * *&amp;lt;/code&amp;gt;&amp;lt;br/&amp;gt;(midnight on the first weekday on or before the 20th) || The less than or equal to character is allowed for the day-of-month field, must be preceded by a W or Day of Week expression (SUN-SAT) and followed by a day of month ordinal. It allows you to specify constructs such as &amp;quot;the first weekday on or before the 20th&amp;quot; or &amp;quot;the first Wednesday on or before the 9th&amp;quot;. &#039;&#039;&#039;As of Obsidian 6.2.0.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Greater Than Or Equal To (≥) || &amp;lt;code&amp;gt;0 0 MON≥5 * * *&amp;lt;/code&amp;gt;&amp;lt;br/&amp;gt;(midnight on the first Monday on or after the 5th) || The greater than or equal to character is allowed for the day-of-month field, must be preceded by a W or Day of Week expression (SUN-SAT) and followed by a day of month ordinal. It allows you to specify constructs such as &amp;quot;the first Monday on or after the 5th&amp;quot; or &amp;quot;the first weekday on or after the 22nd&amp;quot;. &#039;&#039;&#039;As of Obsidian 6.2.0.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Closest to (~) || &amp;lt;code&amp;gt;0 0 THU~5 * * *&amp;lt;/code&amp;gt;&amp;lt;br/&amp;gt;(midnight on the Thursday closest to the 5th) || The tilde is allowed for the day-of-month field, must be preceded by a W or Day of Week expression (SUN-SAT) and followed by a day of month ordinal. It allows you to specify constructs such as &amp;quot;the Sunday closest to the 8th&amp;quot; or &amp;quot;the weekday closest to the 14th&amp;quot;. &#039;&#039;&#039;As of Obsidian 6.3.0.&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Examples==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Cron Pattern&lt;br /&gt;
!Explanation&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;white-space:nowrap&amp;quot;|&amp;lt;code&amp;gt;5 8 TUE≤14 2 * * America/Toronto&amp;lt;/code&amp;gt;&lt;br /&gt;
|At 8:05AM on the Tuesday on or before the 14th during February in Eastern Standard Time&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;white-space:nowrap&amp;quot;|&amp;lt;code&amp;gt;0 12 W≥1,W≥15 * *&amp;lt;/code&amp;gt;&lt;br /&gt;
|At 12:00PM on the weekday on or after the 1st, the weekday on or after the 15th&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;white-space:nowrap&amp;quot;|&amp;lt;code&amp;gt;0 16 SUN~5,W~10 * *&amp;lt;/code&amp;gt;&lt;br /&gt;
|At 4:00PM on the Sunday closest to the 5th, the weekday closest to the 10th&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Shortcut Expressions =&lt;br /&gt;
&lt;br /&gt;
Obsidian supports some shorthand expressions to easily input common expressions. These start with the at sign (@).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable leftAlignTable&amp;quot;&lt;br /&gt;
! Shortcut || Long-Form Expression || Description&lt;br /&gt;
|-&lt;br /&gt;
| @yearly || 0 0 1 1 * || Run once a year, on Jan 1st at midnight.&lt;br /&gt;
|-&lt;br /&gt;
| @annually || 0 0 1 1 * || Same as @yearly.&lt;br /&gt;
|-&lt;br /&gt;
| @monthly || 0 0 1 * * || Run once a month, on the 1st at midnight.&lt;br /&gt;
|-&lt;br /&gt;
| @weekly || 0 0 * * 0 || Run once a week, on Sunday at midnight.&lt;br /&gt;
|-&lt;br /&gt;
| @daily || 0 0 * * * || Run once a day, at midnight.&lt;br /&gt;
|-&lt;br /&gt;
| @midnight || 0 0 * * * || Same as @daily.&lt;br /&gt;
|-&lt;br /&gt;
| @hourly  || 0 * * * * || Run on the hour, every hour.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Recurrence =&lt;br /&gt;
&lt;br /&gt;
In addition to Cron syntax, Obsidian supports special recurrence syntax to allow expression of recurring intervals that can&#039;t be defined in a single Cron pattern. For example, if you wanted to run a job every 7 minutes, every 2 weeks or every 5 months, none of these can be expressed in a single Cron pattern.&lt;br /&gt;
&lt;br /&gt;
Obsidian recurrence patterns always start with &amp;lt;code&amp;gt;@recur&amp;lt;/code&amp;gt; and are followed by an interval type. Recurring intervals are calculated from the last execution time, if available. Otherwise, the initial execution will be at the start time of the job schedule. This means that when you schedule a new recurrence, it will execute first when the schedule starts, and then at the interval specified after that.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; If an ad hoc job is submitted or a failed job is resubmitted, it will cause the next recurrence interval to be delayed since the next execution time will be based on the last available job.&lt;br /&gt;
&lt;br /&gt;
== Interval Types ==&lt;br /&gt;
* &amp;lt;code&amp;gt;min&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;minute&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;minutes&amp;lt;/code&amp;gt; - minutes&lt;br /&gt;
* &amp;lt;code&amp;gt;h&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;hour&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;hours&amp;lt;/code&amp;gt; - hours&lt;br /&gt;
* &amp;lt;code&amp;gt;d&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;day&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;days&amp;lt;/code&amp;gt; - days&lt;br /&gt;
* &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;week&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;weeks&amp;lt;/code&amp;gt; - weeks&lt;br /&gt;
* &amp;lt;code&amp;gt;mon&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;month&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;months&amp;lt;/code&amp;gt; - months&lt;br /&gt;
&lt;br /&gt;
==Sample Patterns==&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;@recur 7 minutes&amp;lt;/code&amp;gt; - every 7 minutes&lt;br /&gt;
* &amp;lt;code&amp;gt;@recur 7 h&amp;lt;/code&amp;gt; - every 7 hours&lt;br /&gt;
* &amp;lt;code&amp;gt;@recur 3 day&amp;lt;/code&amp;gt; - every 3 days&lt;br /&gt;
* &amp;lt;code&amp;gt;@recur 3 weeks&amp;lt;/code&amp;gt; - every 3 weeks&lt;br /&gt;
* &amp;lt;code&amp;gt;@recur 5 mon&amp;lt;/code&amp;gt; - every 5 months&lt;br /&gt;
&lt;br /&gt;
==Recurrence Start DateTime==&lt;br /&gt;
The recurrence pattern may include an optional start datetime. This datetime would be used as a basis for calculating recurrence (if before the effective date of the schedule window), or as the initial recurrence datetime (if after the effective datetime of the schedule window). Without this value, any recurrence pattern without history for the given schedule window will have its first runtime at the effective datetime of the schedule window. When the schedule effective date is greater than the start datetime specified, the first calculated recurrence value that is greater than or equal to the schedule effective datetime will be the first scheduled runtime. See examples below.&lt;br /&gt;
&lt;br /&gt;
This functionality can be used to ensure a job fires on consistent recurrence intervals when migrating between environments, perhaps using the [[Initializing and Restoring]] functionality.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DateTime Format&#039;&#039;&#039; is &amp;lt;code&amp;gt;yyyy-MM-dd HH:mm&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Examples===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Recur Pattern&lt;br /&gt;
!Effective Date&lt;br /&gt;
!End Date&lt;br /&gt;
!First Runtime&lt;br /&gt;
!Explanation&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;white-space:nowrap&amp;quot;|&amp;lt;code&amp;gt;@recur 5 month 2015-02-01 02:00&amp;lt;/code&amp;gt;&lt;br /&gt;
|style=&amp;quot;text-align:center;white-space:nowrap&amp;quot;|&amp;lt;code&amp;gt;2015-03-15 12:00&amp;lt;/code&amp;gt;&lt;br /&gt;
|style=&amp;quot;text-align:center;white-space:nowrap&amp;quot;|&amp;amp;infin;&lt;br /&gt;
|style=&amp;quot;text-align:center;white-space:nowrap&amp;quot;|&amp;lt;code&amp;gt;2015-07-01 02:00&amp;lt;/code&amp;gt;&lt;br /&gt;
|The recurrence start datetime is less than the effective date, but the first calculated recurrence is greater than the schedule effective window.&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;white-space:nowrap&amp;quot;|&amp;lt;code&amp;gt;@recur 20 weeks 2015-01-15 00:00&amp;lt;/code&amp;gt;&lt;br /&gt;
|style=&amp;quot;text-align:center;white-space:nowrap&amp;quot;|&amp;lt;code&amp;gt;2015-12-20 00:00&amp;lt;/code&amp;gt;&lt;br /&gt;
|style=&amp;quot;text-align:center;white-space:nowrap&amp;quot;|&amp;amp;infin;&lt;br /&gt;
|style=&amp;quot;text-align:center;white-space:nowrap&amp;quot;|&amp;lt;code&amp;gt;2016-03-10 00:00&amp;lt;/code&amp;gt;&lt;br /&gt;
|The recurrence start datetime is less than the effective date, but the first (&amp;lt;code&amp;gt;2015-06-04 00:00&amp;lt;/code&amp;gt;) and second(&amp;lt;code&amp;gt;2015-10-22 00:00&amp;lt;/code&amp;gt;) calculated recurrences are also less than the schedule effective window.&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;white-space:nowrap&amp;quot;|&amp;lt;code&amp;gt;@recur 20 weeks 2015-01-15 00:00&amp;lt;/code&amp;gt;&lt;br /&gt;
|style=&amp;quot;text-align:center;white-space:nowrap&amp;quot;|&amp;lt;code&amp;gt;2015-12-20 00:00&amp;lt;/code&amp;gt;&lt;br /&gt;
|style=&amp;quot;text-align:center;white-space:nowrap&amp;quot;|&amp;lt;code&amp;gt;2016-01-31 23:59&amp;lt;/code&amp;gt;&lt;br /&gt;
|style=&amp;quot;text-align:center;white-space:nowrap&amp;quot;|&#039;&#039;&#039;None&#039;&#039;&#039;&lt;br /&gt;
|The recurrence start datetime is less than the effective date, as are the first (&amp;lt;code&amp;gt;2015-06-04 00:00&amp;lt;/code&amp;gt;) and second (&amp;lt;code&amp;gt;2015-10-22 00:00&amp;lt;/code&amp;gt;) calculated recurrences. The third calculated recurrence (&amp;lt;code&amp;gt;2016-03-10 00:00&amp;lt;/code&amp;gt;) is greater than the effective date but is also greater than the &#039;&#039;end date&#039;&#039;. This results in no scheduled executions.&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;text-align:center;white-space:nowrap&amp;quot;|&amp;lt;code&amp;gt;@recur 20 weeks 2015-01-15 00:00&amp;lt;/code&amp;gt;&lt;br /&gt;
|style=&amp;quot;text-align:center;white-space:nowrap&amp;quot;|&amp;lt;code&amp;gt;2015-01-01 00:00&amp;lt;/code&amp;gt;&lt;br /&gt;
|style=&amp;quot;text-align:center;white-space:nowrap&amp;quot;|&amp;lt;code&amp;gt;2016-01-31 23:59&amp;lt;/code&amp;gt;&lt;br /&gt;
|style=&amp;quot;text-align:center;white-space:nowrap&amp;quot;|&amp;lt;code&amp;gt;2015-01-15 00:00&amp;lt;/code&amp;gt;&lt;br /&gt;
|The recurrence start datetime is greater than the effective date and less than the end date. Initial recurrence will start at the specified start datetime.&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Conflicts&amp;diff=4142</id>
		<title>Conflicts</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Conflicts&amp;diff=4142"/>
		<updated>2026-07-31T14:49:49Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian supports completely customizable job conflict configuration combined with custom prioritization. The web application contains an [[Admin_Job_Conflicts|easy-to-use interface]] to configure these conflicts.&lt;br /&gt;
&lt;br /&gt;
Any number of jobs can be configured to not run concurrently, even if scheduled to run at the same time. When jobs are set as conflicting and are scheduled to run at the same time, the chosen prioritization will determine the order of execution of the scheduled jobs. Any termination state, either success or failure, will release the next lower priority job to complete.  These conflicts work even across multiple hosts. &lt;br /&gt;
&lt;br /&gt;
If a job which isn&#039;t using the &#039;&#039;Conflicted&#039;&#039; recovery type cannot be executed within its [[Job_Features#Recovery|Pickup Buffer]], it will be marked as &#039;&#039;Conflict Missed&#039;&#039;, though it can be resubmitted later if desired. When you configure job conflicts, you will want to choose [[Job_Features#Recovery|Recovery]] settings carefully (&#039;&#039;Recovery Type&#039;&#039; and &#039;&#039;Pickup Buffer&#039;&#039;).  &lt;br /&gt;
&lt;br /&gt;
== Using Conflicted Recovery Type ==&lt;br /&gt;
If you are using &#039;&#039;Conflicted&#039;&#039; recovery type, the &#039;&#039;Pickup Buffer&#039;&#039; minute setting is not used when determining whether to run a job that had been conflicted.  When the higher priority conflict(s) clear, a job with &#039;&#039;Conflicted&#039;&#039; recovery will always run, though the &#039;&#039;Pickup Buffer&#039;&#039; is still used in other scenarios, such as outages. &lt;br /&gt;
&lt;br /&gt;
== Using Non-Conflicted Recovery Types ==&lt;br /&gt;
When using &#039;&#039;Recovery Types&#039;&#039; other than &#039;&#039;Conflicted&#039;&#039;, in the event of a conflict, if the waiting lower-priority job cannot be picked up and started within the configured number of &#039;&#039;Pickup Buffer Minutes&#039;&#039; past the original scheduled time, the scheduled job will be marked as &#039;&#039;Conflict Missed&#039;&#039; and will not be run.&lt;br /&gt;
&lt;br /&gt;
== Jobs in Multiple Conflict Groups ==&lt;br /&gt;
A job can be put into multiple conflict groups, each having their own separate priority ordering.&lt;br /&gt;
&lt;br /&gt;
When selecting available non-conflicted jobs to run, Obsidian inspects the conflict groups in the order provided when they are saved (from left to right in the user interface), and selects the highest priority available job before moving onto the next conflict group. When priority is significant and varies across different groups, ensure your conflict groups are in the desired order.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
To demonstrate, in the example below, if Jobs 2 and 3 are ready for execution, the configuration below will cause Job 3 to be executed first since it is found in the first conflict group. If the conflict groups were saved in the reverse order, Job 2 would be executed first. In addition, if Job 1 were also available for execution in the example below, it would prevent Job 3 from executing, which would result in Job 2 running simultaneously with Job 1.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:MultiConflictExample.png]]&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Clustering&amp;diff=4141</id>
		<title>Clustering</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Clustering&amp;diff=4141"/>
		<updated>2026-07-31T14:49:48Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian provides zero-configuration clustering. If you want to add a new scheduler to the cluster or replace an existing node, simply start it up pointing to your Obsidian database, and it will join the pool and begin executing jobs immediately. You may wish to consult [[Recovery_%26_Failover|Recovery &amp;amp; Failover]] for additional details on scheduler behaviour.&lt;br /&gt;
&lt;br /&gt;
Obsidian&#039;s clustering works by coordinating effort of various nodes through a central relational database such as MySQL or Oracle. Each set of Obsidian tables corresponds to a separate cluster instance. For example, you may have separate QA and production databases. If you run multiple Obsidian schedulers pointing to the same database, they will run in the same cluster. This can also be accomplished by having Obsidian tables in the same database but using a separate [[Advanced_Configuration#Table Prefixes|table prefix]]. &lt;br /&gt;
&lt;br /&gt;
No cluster configuration is ever required, allowing you to adapt quickly to new needs.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;All scheduler nodes, any [[Deployment_Diagram|deployed]] Web Admin WARs and the database should be time synchronized from the same source and to the same timezone.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Scheduler nodes may have different timezones that adhere to the same time rules (are functionally equivalent). This is implemented via the [[https://docs.oracle.com/javase/6/docs/api/java/util/TimeZone.html#hasSameRules(java.util.TimeZone) TimeZone.hasSameRules(TimeZone)]] method.&lt;br /&gt;
&lt;br /&gt;
== Load Sharing Algorithm ==&lt;br /&gt;
The clustering algorithm in Obsidian is designed to take full advantage of the available nodes in the cluster. The scheduler takes into account how many other nodes are running, and how many jobs need to be run at the current time in order to provide load balancing. That is one key difference in Obsidian since it values load-sharing over second-level precision, since most applications don&#039;t require it. Other schedulers, by contrast, do clustering by having all nodes immediately try to claim a job for execution, which can result in one node claiming multiple jobs while others remain idle.&lt;br /&gt;
&lt;br /&gt;
You can add new cluster nodes at any time and they will automatically join the pool, and the load-sharing and scheduling algorithm will adapt right away.&lt;br /&gt;
&lt;br /&gt;
==Host Restrictions ==&lt;br /&gt;
Jobs can be restricted to run only on specified hosts. This allows you to use Obsidian in a multi-host environment, even when certain jobs can only run on one of the hosts in the cluster. More details can be found at [[Job Features#Host Restrictions|Job Features]], or you can view details on the corresponding [[Admin_Jobs#Adding_.26_Editing_Jobs|configuration screen]].&lt;br /&gt;
&lt;br /&gt;
==Host Preference ==&lt;br /&gt;
In addition to host restrictions, Obsidian supports the concept of host preference. For each job, you can select this option to enable prioritizing specific hosts for job execution. When a job configured with this setting is ready for execution, Obsidian will select the most preferred host to execute the job on, and if no preferred host is available, execution will fall back to any available host. This is configurable on the [[Admin_Jobs#Adding_.26_Editing_Jobs|job configuration screen]].&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Chaining&amp;diff=4140</id>
		<title>Chaining</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Chaining&amp;diff=4140"/>
		<updated>2026-07-31T14:49:48Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian supports dynamic job chaining between two jobs.  All chaining configuration is managed in the [[Admin Job Chains|job chains]] portion of the admin web application.  &lt;br /&gt;
&lt;br /&gt;
Chaining can be triggered simply by the state of the queued job, or conditionally, based on the saved results of a successfully completed job.&lt;br /&gt;
&lt;br /&gt;
A job can be chained to any other job.  By default, chaining between two jobs will trigger when the source job either completes or fails, but any source termination state can be selected to trigger the chain.  Chain configurations can be disabled and re-enabled at any time.  Chaining can also be constrained to a schedule, using a standard cron schedule. &lt;br /&gt;
&lt;br /&gt;
Chaining will only trigger for a target job if the job has the state Enabled or Chain Active at the trigger time.  A job that is Disabled or Ad Hoc Active will not be chained.&lt;br /&gt;
&lt;br /&gt;
=Conditional Chaining=&lt;br /&gt;
&lt;br /&gt;
Release 1.4 introduced conditional chaining. This feature allows for jobs to be triggered when a successfully completed source job saves [[Implementing_Jobs#Job_Results|job results]] which match a set of defined conditions. When setting up the conditional evaluations, you specify one or more variable evaluations that must be matched to cause the chain to fire. A condition is comprised of the variable name, an operator, and one or more values &lt;br /&gt;
&lt;br /&gt;
For numeric operators, your conditions are coerced to the numeric value types that your source job saves (e.g. integer, etc.).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; All conditions must be met for the chain to be successfully triggered (i.e. they are ANDed together). Additionally, operators match if &#039;&#039;any&#039;&#039; saved value matches the operator. This is important to remember if a job saves more than one value under the same variable name.&lt;br /&gt;
&lt;br /&gt;
The supported operators are:&lt;br /&gt;
* &#039;&#039;&#039;Equals/Not Equals&#039;&#039;&#039; - A direct equality comparison (case-sensitive). If no value is saved, Not Equals evaluates to true.&lt;br /&gt;
* &#039;&#039;&#039;Exists/Not Exists&#039;&#039;&#039; - Uses presence of a saved variable to match.&lt;br /&gt;
* &#039;&#039;&#039;In/Not In&#039;&#039;&#039; - Triggered if &#039;&#039;any&#039;&#039; saved job result value under the name matches a defined value (case-sensitive). If no value is saved, Not In evaluates to true.&lt;br /&gt;
* &#039;&#039;&#039;Contains&#039;&#039;&#039; - String contains (case-sensitive).&lt;br /&gt;
* &#039;&#039;&#039;Starts With/Ends With&#039;&#039;&#039; - String starts with/ends with match (case-sensitive). An equal string will also trigger either of these operators.&lt;br /&gt;
* &#039;&#039;&#039;Regexp&#039;&#039;&#039; - Regular expression match based on Java&#039;s Pattern class.&lt;br /&gt;
* &#039;&#039;&#039;Less Than/Less Than Or Equal/Greater Than/Greater Than Or Equal&#039;&#039;&#039; - Numeric only: values are coerced to an integer or decimal value.&lt;br /&gt;
&lt;br /&gt;
=Chaining Results=&lt;br /&gt;
All chaining evaluations are stored.  If chaining is triggered, or if it fails based on a conditional evaluation, the [[Admin Job Activity|job activity]] view shows which jobs a source job triggered and also which job triggered a chained job.  When chaining misses because the source state didn&#039;t match any of those configured, target job is in an unchainable state, chaining configuration is disabled or source completion time was outside the constraints of a provided chaining schedule, a detail record with state Chain Skipped will still be available for review with the detail of why chaining was not triggered.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Built-in_Jobs&amp;diff=4139</id>
		<title>Built-in Jobs</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Built-in_Jobs&amp;diff=4139"/>
		<updated>2026-07-31T14:49:47Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian comes bundled with free [[Admin_Jobs|jobs]] for common tasks. These are provided in addition to [[Scripting Jobs]] to reduce implementation and testing time for common job functions.&lt;br /&gt;
&lt;br /&gt;
We have open-sourced our built-in convenience jobs under the [[https://opensource.org/license/mit/ MIT License]]. In the root of the installation folder, you can find the source in &amp;lt;code&amp;gt;obsidian-builtin-job-src.jar&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= File Processing Jobs =&lt;br /&gt;
&lt;br /&gt;
These jobs are provided to provide common basic file operations, such as file cleanup (deletion) and archiving. These jobs can be used to reduce the amount of code your organization needs to write.&lt;br /&gt;
&lt;br /&gt;
== File Cleanup Job ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.job.maint.FileCleanupJob&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This job deletes files in specified paths based on one or more file masks (using regular expressions). &lt;br /&gt;
&lt;br /&gt;
It supports the following configuration options:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Directory (1 or more Strings)&#039;&#039; - A directory to scan for files. If recursive processing is enabled, child directories are also scanned.&lt;br /&gt;
* &#039;&#039;File Mask (0 or more Strings)&#039;&#039; - A regular expression that files must match to be deleted. This is based on &amp;lt;code&amp;gt;java.util.regex.Pattern&amp;lt;/code&amp;gt;. To match all files, remove all configured values or use &amp;quot;.*&amp;quot; without quotes. If multiple values are used, files are eligible for deletion if they match any of the file masks.&lt;br /&gt;
* &#039;&#039;Abort on Deletion Failure (Boolean)&#039;&#039; - If deletion fails, should we fail the job?&lt;br /&gt;
* &#039;&#039;File Size Minimum (String)&#039;&#039; - The minimum size in bytes a file must be to be deleted. File sizes can be specified in bytes, kilobytes, megabytes or gigabytes (e.g. 10b, 100kb, 20mb, 2gb).&lt;br /&gt;
* &#039;&#039;File Size Maximum (String)&#039;&#039; - The maximum size in bytes a file must be to be deleted. File sizes can be specified in bytes, kilobytes, megabytes or gigabytes (e.g. 10b, 100kb, 20mb, 2gb).&lt;br /&gt;
* &#039;&#039;Minimum Time Since Modified (String)&#039;&#039; - The minimum age of a file to be deleted, based on the modified time. This can be specified in seconds, minutes, hours and days (e.g. 10s, 5m, 24h, 2d). &lt;br /&gt;
* &#039;&#039;Recursive (Boolean)&#039;&#039; - Whether files in subdirectories of the configured directories should be checked for matching files.&lt;br /&gt;
&lt;br /&gt;
=== Customization ===&lt;br /&gt;
If you wish to tweak or customize behaviour of this job, it can be subclassed. The following methods may be overridden or extended by calling &#039;&#039;super&#039;&#039; methods:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;onStart&#039;&#039; - Called on job startup.&lt;br /&gt;
* &#039;&#039;onEnd&#039;&#039; - Called on job end, regardless of success or failure.&lt;br /&gt;
* &#039;&#039;shouldDelete&#039;&#039; - Determines if a file should be deleted.&lt;br /&gt;
* &#039;&#039;deleteMatchingFile&#039;&#039; - Deletes a matching file and saves a job result for its path.&lt;br /&gt;
* &#039;&#039;matchesAge&#039;&#039; - Determines if a file matches any age conditions (i.e. &#039;&#039;Minimum Time Since Modified&#039;&#039;), or true if none are configured.&lt;br /&gt;
* &#039;&#039;matchesMask&#039;&#039; - Determines if a file matches any configured file masks, or true if none are configured.&lt;br /&gt;
* &#039;&#039;matchesSize&#039;&#039; - Determines if a file matches size conditions, or true if none are configured.&lt;br /&gt;
* &#039;&#039;processDirectory&#039;&#039; - Enumerates files in configured directories and calls &#039;&#039;shouldDelete&#039;&#039; and &#039;&#039;deleteMatchingFile&#039;&#039; as necessary.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Configuration&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Image:FileCleanup.png]]&lt;br /&gt;
&lt;br /&gt;
== File Archive Job ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.job.maint.FileArchiveJob&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This job archives files passed in via a chained job&#039;s saved results. Therefore, it is intended to be used as a chained job only. One or more accessible absolute file paths can be passed in via job results called &#039;file&#039;. Each one will be processed for archiving. For example, if you have a job that generates two files called &#039;customers.txt&#039; and &#039;orders.txt&#039;, you will use &amp;lt;code&amp;gt;Context.saveJobResult(String, Object)&amp;lt;/code&amp;gt; to save a value for each absolute file path (e.g. &#039;C:/customers.txt&#039; and &#039;C:/orders.txt&#039;).&lt;br /&gt;
&lt;br /&gt;
It supports the following configuration options:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Archive Directory (1 or more Strings)&#039;&#039; - A directory to where an archive copy is placed.&lt;br /&gt;
* &#039;&#039;Rename Pattern (String)&#039;&#039; - How should we name the archive file? Rename patterns support the following tokens delimited by &amp;lt; and &amp;gt;: &lt;br /&gt;
** &amp;lt;code&amp;gt;&amp;lt;filename&amp;gt;&amp;lt;/code&amp;gt; is the full original file name (e.g. &#039;abc.v1.txt&#039;)&lt;br /&gt;
** &amp;lt;code&amp;gt;&amp;lt;basename&amp;gt;&amp;lt;/code&amp;gt; is the file with the last extension stripped (e.g. &#039;abc.v1&#039;)&lt;br /&gt;
** &amp;lt;code&amp;gt;&amp;lt;ext&amp;gt;&amp;lt;/code&amp;gt; is the last file extension (e.g. &#039;txt&#039;)&lt;br /&gt;
** &amp;lt;code&amp;gt;&amp;lt;ts:dateformat&amp;gt;&amp;lt;/code&amp;gt; corresponds to a timestamp formatted in the supplied SimpleDateFormat pattern (e.g. &amp;lt;code&amp;gt;&amp;lt;ts:yyyy-MM-dd-HH-mm:ss&amp;gt;&amp;lt;/code&amp;gt;)&lt;br /&gt;
** other literals outside of &amp;lt; or &amp;gt;, or inside of &amp;lt; and &amp;gt; but not matching any preceding tokens. &lt;br /&gt;
** For example, to archive a GZIP version of a file with a timestamp with the extension &#039;gz&#039;, you can use the rename pattern &amp;lt;code&amp;gt;&amp;lt;basename&amp;gt;.&amp;lt;ts:yyyy-MM-dd-HH-mm-ss&amp;gt;.&amp;lt;ext&amp;gt;.gz&amp;lt;/code&amp;gt;.&lt;br /&gt;
** To maintain the original name, simply use the pattern &amp;lt;code&amp;gt;&amp;lt;filename&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &#039;&#039;Delete Original File (Boolean)&#039;&#039; - Should we remove the original file after archiving?&lt;br /&gt;
* &#039;&#039;GZIP (Boolean)&#039;&#039; - Should the target archive file be compressed using GZIP?&lt;br /&gt;
* &#039;&#039;Overwrite Target File (Boolean)&#039;&#039; - If the archive file to be created already exists, should we overwrite it, or fail instead?&lt;br /&gt;
* &#039;&#039;File Size Minimum (String)&#039;&#039; - The minimum size in bytes a file must be to be deleted. File sizes can be specified in bytes, kilobytes, megabytes or gigabytes (e.g. 10b, 100kb, 20mb, 2gb). As of Obsidian 6.0.1&lt;br /&gt;
* &#039;&#039;File Size Maximum (String)&#039;&#039; - The maximum size in bytes a file must be to be deleted. File sizes can be specified in bytes, kilobytes, megabytes or gigabytes (e.g. 10b, 100kb, 20mb, 2gb). As of Obsidian 6.0.1&lt;br /&gt;
* &#039;&#039;Minimum Time Since Modified (String)&#039;&#039; - The minimum age of a file to be deleted, based on the modified time. This can be specified in seconds, minutes, hours and days (e.g. 10s, 5m, 24h, 2d).  As of Obsidian 6.0.1&lt;br /&gt;
&lt;br /&gt;
=== Customization ===&lt;br /&gt;
If you wish to tweak or customize behaviour of this job, it can be subclassed. The following methods may be overridden or extended by calling &#039;&#039;super&#039;&#039; methods:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;onStart&#039;&#039; - Called on job startup.&lt;br /&gt;
* &#039;&#039;onEnd&#039;&#039; - Called on job end, regardless of success or failure.&lt;br /&gt;
* &#039;&#039;determineArchiveFilename&#039;&#039; - Determines the name of the archive file based on the configured &amp;quot;Rename Pattern&amp;quot;.&lt;br /&gt;
* &#039;&#039;processFile&#039;&#039; - For each input file, determines the archive file name and copies the original file to the archive.&lt;br /&gt;
* &#039;&#039;processDelete&#039;&#039; - If the original file is to be deleted, this method handles deleting the original file. If deletion fails, the job will fail, so if you wish to change his behaviour, you can override this method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Configuration&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Image:FileArchiveJobConfig.png]]&lt;br /&gt;
&lt;br /&gt;
== File Scanner Job ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.job.maint.FileScannerJob&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This job locates files in a directory matching one or more file masks (using Java regular expressions) and stores the result via source job results stored under the name &amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt; as absolute paths (Strings).&lt;br /&gt;
&lt;br /&gt;
This job is intended to be conditionally chained to a job that knows how to process the results. &lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Go File Pattern&#039;&#039; may used to ensure that files in the configured &#039;&#039;Directory&#039;&#039; are only picked up and saved if an expected token file exists. If &#039;&#039;Delete Go File&#039;&#039; is set to true, any found token files will be deleted. If this deletion fails, the job will abort and no &amp;lt;code&amp;gt;file&amp;lt;/code&amp;gt; results are saved. Successfully deleted go files are stored as job results under the name &amp;lt;code&amp;gt;deletedGoFile&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The job supports the following configuration options:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Directory (String)&#039;&#039; - A directory to scan for files.&lt;br /&gt;
* &#039;&#039;File Pattern (1 or more Strings)&#039;&#039; - One or more file patterns (using &amp;lt;code&amp;gt;java.util.regex.Pattern&amp;lt;/code&amp;gt;) that a file name must match to be considered a match. If any one of the configured patterns is a match, the file is considered a match.&lt;br /&gt;
* &#039;&#039;Go File Pattern (String)&#039;&#039; - A file pattern (using &amp;lt;code&amp;gt;java.util.regex.Pattern&amp;lt;/code&amp;gt;) that must match at least one file in in the &#039;&#039;Go File Directory&#039;&#039; (or main &#039;&#039;Directory&#039;&#039; if not supplied) in order for files to be processed and saved as job results. &lt;br /&gt;
* &#039;&#039;Go File Directory (optional String)&#039;&#039; - The directory where we look for files matching the &#039;&#039;Go File Pattern&#039;&#039;. If missing, the value for &#039;&#039;Directory&#039;&#039; is used.&lt;br /&gt;
* &#039;&#039;Delete Go File (Boolean)&#039;&#039; - If we find one or more go files, should we delete them? If deletion fails, the job will fail.&lt;br /&gt;
* &#039;&#039;File Size Minimum (String)&#039;&#039; - The minimum size in bytes a file must be to be deleted. File sizes can be specified in bytes, kilobytes, megabytes or gigabytes (e.g. 10b, 100kb, 20mb, 2gb). As of Obsidian 6.0.1&lt;br /&gt;
* &#039;&#039;File Size Maximum (String)&#039;&#039; - The maximum size in bytes a file must be to be deleted. File sizes can be specified in bytes, kilobytes, megabytes or gigabytes (e.g. 10b, 100kb, 20mb, 2gb). As of Obsidian 6.0.1&lt;br /&gt;
* &#039;&#039;Minimum Time Since Modified (String)&#039;&#039; - The minimum age of a file to be deleted, based on the modified time. This can be specified in seconds, minutes, hours and days (e.g. 10s, 5m, 24h, 2d).  As of Obsidian 6.0.1&lt;br /&gt;
&lt;br /&gt;
=== Customization ===&lt;br /&gt;
If you wish to tweak or customize behaviour of this job, it can be subclassed. The following methods may be overridden or extended by calling &#039;&#039;super&#039;&#039; methods:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;onStart&#039;&#039; - Called on job startup.&lt;br /&gt;
* &#039;&#039;onEnd&#039;&#039; - Called on job end, regardless of success or failure.&lt;br /&gt;
* &#039;&#039;listFiles&#039;&#039; - Lists files within the directory using the supplied patterns.&lt;br /&gt;
* &#039;&#039;postProcessFiles&#039;&#039; - This is a hook method called after all job results are saved and go files are deleted if applicable. It is called with all valid files that were found.&lt;br /&gt;
* &#039;&#039;deleteGoFiles&#039;&#039; - Deletes all found go files, saving a result if successful and throwing a RuntimeException if failing. This can be overriden to customize handling of failures.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Configuration&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Image:FileScannerConfig.png]]&lt;br /&gt;
&lt;br /&gt;
= Maintenance Jobs =&lt;br /&gt;
&lt;br /&gt;
These jobs are provided to help maintain the Obsidian installation. Automatic schedule configuration can be disabled via &amp;lt;code&amp;gt;com.carfey.obsidian.standardOutputStreamsEventHook.enabled=true&amp;lt;/code&amp;gt; configuration.&lt;br /&gt;
&lt;br /&gt;
== Job History Cleanup Job ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.job.maint.JobHistoryCleanupJob&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This job will delete job history and related records beyond a configurable age in days. This is useful for keeping the database compact and clearing out old, unneeded data. &lt;br /&gt;
&lt;br /&gt;
The value for &amp;lt;code&amp;gt;maxAgeDays&amp;lt;/code&amp;gt; corresponds to the maximum age for records to retain, including expired job schedules. By default it is set to 366, but you may configure it to any desired value.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; We recommend high volume users schedule the job weekly or less frequently and to schedule it for a non-peak time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 5.2.1&#039;&#039;&#039;, this job is configured during new installations to run &#039;@daily&#039; keeping the last 380 days of history.&lt;br /&gt;
&lt;br /&gt;
== Log Cleanup Job ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.job.maint.LogCleanupJob&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This job will delete log database records beyond a configurable age in days. This is useful for keeping the database compact and clearing out old, unneeded data. &lt;br /&gt;
&lt;br /&gt;
The value for &amp;lt;code&amp;gt;maxAgeDays&amp;lt;/code&amp;gt; corresponds to the maximum age for records to retain. By default it is set to 365, but you may configure it to any desired value.&lt;br /&gt;
&lt;br /&gt;
The value for &amp;lt;code&amp;gt;level&amp;lt;/code&amp;gt; corresponds to the logging level of events to delete. You may specify multiple values, and the &amp;quot;ALL&amp;quot; option will result in all records matching the age setting being deleted. Valid levels to configure are: &amp;lt;code&amp;gt;FATAL&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;ERROR&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;WARNING&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;INFO&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;TRACE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
It is common to only delete lower severity level events, such as &amp;lt;code&amp;gt;INFO&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;TRACE&amp;lt;/code&amp;gt;, but retain higher severity messages.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; We recommend high volume users schedule the job weekly or less frequently and to schedule it for a non-peak time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 5.2.1&#039;&#039;&#039;, this job is configured during new installations as follows:&lt;br /&gt;
* 1:00 AM for DEBUG/TRACE keeping the last 30 days of event logs.&lt;br /&gt;
* 1:30 AM for INFO keeping the last 60 days of event logs.&lt;br /&gt;
* 2:00 AM for WARNING/ERROR keeping the last 120 days of event logs.&lt;br /&gt;
* 2:30 AM for FATAL keeping the last 185 days of event logs.&lt;br /&gt;
&lt;br /&gt;
== Notification Cleanup Job ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Introduced in Obsidian 4.9.0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.job.maint.NotificationCleanupJob&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This job will delete notification database records beyond a configurable age in days. Optionally you may constrain this to specific subscribers. This is useful for keeping the database compact and clearing out old, unneeded data. &lt;br /&gt;
&lt;br /&gt;
The value for &amp;lt;code&amp;gt;maxAgeDays&amp;lt;/code&amp;gt; corresponds to the maximum age for records to retain. By default it is set to 365, but you may configure it to any desired value.&lt;br /&gt;
&lt;br /&gt;
The value for &amp;lt;code&amp;gt;subscriber&amp;lt;/code&amp;gt; corresponds to the subscriber address(es) of whose notifictaions should be deleted. No validation is performed against this value to allow for subscriber changes without requiring corresponding job configuration changes.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; We recommend high volume users schedule the job weekly or less frequently and to schedule it for a non-peak time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 5.2.1&#039;&#039;&#039;, this job is configured during new installations to run at 3:30 AM on the 1st of the month keeping the last 120 days of history.&lt;br /&gt;
&lt;br /&gt;
== Disabled Job Cleanup Job ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.job.maint.DisabledJobCleanupJob&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This job cleans up jobs that have been disabled for the last configured number of days and have no future non-disabled schedule windows.&lt;br /&gt;
&lt;br /&gt;
The value for &amp;lt;code&amp;gt;maxDisabledDays&amp;lt;/code&amp;gt; corresponds to how many days in the past a job must have been disabled to be a candidate for being deleted.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; We recommend high volume users schedule the job weekly or less frequently and to schedule it for a non-peak time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 5.2.1&#039;&#039;&#039;, this job is configured during new installations to run at 3:00 AM on the 1st of the month keeping the last 185 days of history.&lt;br /&gt;
&lt;br /&gt;
= Script Job =&lt;br /&gt;
This job is a convenience job to execute any script, command or other executable file on any platform.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.job.script.ScriptFileJob&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This job enables you to specify an existing script, command or other executable to invoke/execute. Validation of the file&#039;s existence and executable state is done only at runtime. You must ensure that the file exists or is accessible via environment variables and in an executable state on all Obsidian Hosts in the cluster or restrict the job to the necessary [[Admin_Jobs#Advanced_Options|Fixed Hosts]]. This job uses [http://docs.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html ProcessBuilder], so refer to it for expected behaviour.&lt;br /&gt;
&lt;br /&gt;
This job supports best-effort interruption as an [[Implementing_Jobs#Interruptable_Jobs|Interruptable job]]. It is best effort in that it only destroys the underlying process that invoked the script, but cannot ensure that anything the script has invoked has terminated.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Script with Arguments (1 or more Strings)&#039;&#039; - The first value is a path to the execution script (or other executable), which is typically fully qualified, or relative to the working directory. The additional values are any required arguments for the command. On *nix, if you are invoking a script, you may need to prefix the command with &amp;quot;./&amp;quot;, e.g. &amp;lt;code&amp;gt;./script.sh&amp;lt;/code&amp;gt;. Corresponds to the [http://docs.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html#ProcessBuilder(java.lang.String...) ProcessBuilder(java.lang.String...) constructor]. &lt;br /&gt;
* &#039;&#039;Copy Obsidian Process&#039; Environment (Boolean)&#039;&#039; - Do you want the runtime environment to be copied from that which is running the Obsidian process? Defaults to &#039;&#039;true&#039;&#039;. If set to &#039;&#039;false&#039;&#039;, the environment is cleared. See [http://docs.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html#environment() ProcessBuilder.environment()]. &lt;br /&gt;
* &#039;&#039;Success Exit Code (Integer)&#039;&#039; - The exit code that designates success. Any exit code not matching this value will result in a job failure. Defaults to &#039;&#039;0&#039;&#039;. &lt;br /&gt;
* &#039;&#039;Working Directory (String)&#039;&#039; - The desired working directory for the script. If no special working directory is required, set to the directory containing the target script. See [http://docs.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html#directory() ProcessBuilder.directory()].&lt;br /&gt;
* &#039;&#039;Environment Parameter Key Value Pairs (0 or more pairs of Strings)&#039;&#039; - Key/Value pairs to be set on the execution environment. These values are set just before execution and will override any existing values copied from the Obsidian Process if &#039;&#039;Copy Obsidian Process&#039; Environment&#039;&#039; was set to true. See [http://docs.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html#environment() ProcessBuilder.environment()].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Configuration&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Image:ScriptFileJobConfig.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Note on Windows ==&lt;br /&gt;
When running the job on Windows, users often encounter an error like the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Cannot run program &amp;quot;run.bat&amp;quot; (in directory &amp;quot;C:\test&amp;quot;): CreateProcess error=2, The system cannot find the file specified.&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This normally means you need to run the command through the Windows command interpretor, using &amp;lt;code&amp;gt;cmd /C&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
To do this, instead of supplying the file to execute as a first value for &#039;&#039;Script with Arguments&#039;&#039;, the supply the values &amp;quot;cmd&amp;quot; and &amp;quot;/C&amp;quot; as separate values, then any others you require, as illustrated below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Script with Arguments:&lt;br /&gt;
cmd&lt;br /&gt;
/C&lt;br /&gt;
run.bat&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= MySqlBackupJob =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is a convenience job to utilize the mysqldump utility to extract a backup and store on the filesystem. A timestamp is appended to the filename prefix specified to protect against overwrites. The optional mysqldump options are not validated and will result in runtime failures if they are invalid. Likewise, all paths specified are not verified and are only required to be available on the scheduler host at runtime. If the mysqldump executable is not found at runtime, change the &#039;&#039;mysqldump Executable&#039;&#039; to have the fully qualified path.&lt;br /&gt;
* &#039;&#039;Fully qualified export path (String)&#039;&#039; - The directory where you want the backups stored.&lt;br /&gt;
* &#039;&#039;Filename prefix (String)&#039;&#039; - The filename prefix to use for the backup files.&lt;br /&gt;
* &#039;&#039;Username (String)&#039;&#039; - The user for the mysqldump command.&lt;br /&gt;
* &#039;&#039;Password (String)&#039;&#039; - The user&#039;s password for the mysqldump command.&lt;br /&gt;
* &#039;&#039;Database (String)&#039;&#039; - The database to backup.&lt;br /&gt;
* &#039;&#039;Hostname or IP Address (optional String)&#039;&#039; - The host of the MySQL database. If not provided, assumes localhost.&lt;br /&gt;
* &#039;&#039;GZip file? (Boolean)&#039;&#039; - Determines whether the backup file is compressed using GZip.&lt;br /&gt;
* &#039;&#039;mysqldump option (0 or more Strings)&#039;&#039; - Any number of desired mysqldump options can be specified.&lt;br /&gt;
* &#039;&#039;mysqldump Executable (String)&#039;&#039; - Fully qualified path to mysqldump executable.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sample Configuration&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:MySqlBackupJobConfig.png]]&lt;br /&gt;
&lt;br /&gt;
= Shell Script Jobs =&lt;br /&gt;
&#039;&#039;&#039;Deprecated.&#039;&#039;&#039; Use [[#Script_Job|Script Job]] instead.&lt;br /&gt;
&lt;br /&gt;
These jobs are provided to give convenient access to shell scripting. &#039;&#039;Not supported on Windows-based platforms.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Shell Script Job ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Deprecated.&#039;&#039;&#039; Use [[#Script_Job|Script Job]] instead.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.job.script.ShellScriptJob&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This job enables you to specify the actual contents of a shell script and have it execute on the runtime host. At runtime, the script will be written to disk, marked as executable and then executed. The script is then cleaned up. The shell script provided should be completely self-sufficient with any desired interpreter directives included and no assumptions or expectations of environment variables.&lt;br /&gt;
&lt;br /&gt;
== Shell Script Execution Job ==&lt;br /&gt;
&#039;&#039;&#039;Deprecated.&#039;&#039;&#039; Use [[#Script_Job|Script Job]] instead.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.job.script.ShellScriptExecutionJob&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This job enables you to specify an existing shell script to execute. Validation of the script&#039;s existence and executable state is done only at runtime. You must ensure that the script exists in an executable state on all Obsidian Hosts in the cluster or restrict the job to the necessary [[Admin_Jobs#Execution_.26_Pickup|Fixed Hosts]].&lt;br /&gt;
&lt;br /&gt;
= Miscellaneous Jobs =&lt;br /&gt;
&lt;br /&gt;
== Database File Export Job ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.job.db.DatabaseFileExportJob&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Introduced in Obsidian 5.1.0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This job produces a file export based on a database connection and a query.&lt;br /&gt;
&lt;br /&gt;
The job supports the following configuration options:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;SQL Query (String)&#039;&#039; - The source query that produces the desired export results.&lt;br /&gt;
* &#039;&#039;JDBC Connection Provider (optional Class)&#039;&#039; - A class implementation of [https://web.obsidianscheduler.com/obsidianapi/com/carfey/ops/job/db/DatabaseConnectionProvider.html DatabaseConnectionProvider] that provides a connection - allows for use of an existing connection pool or other means of obtaining a connection. If not used, must used the URL/username/password configuration for connections.&lt;br /&gt;
* &#039;&#039;JDBC DB URL (optional String)&#039;&#039; - JDBC URL. Relies on JDBC driver class being available on the classpath. If not used, must used the JDBC Connection Provider for connections.&lt;br /&gt;
* &#039;&#039;JDBC DB username (optional String)&#039;&#039; - JBDC database username. Relies on JDBC driver class being available on the classpath. If not used, must used the JDBC Connection Provider for connections.&lt;br /&gt;
* &#039;&#039;JDBC DB password (optional String)&#039;&#039; - JBDC database password. Relies on JDBC driver class being available on the classpath. If not used, must used the JDBC Connection Provider for connections.&lt;br /&gt;
* &#039;&#039;File type (String)&#039;&#039; - One of CSV, JSON, XML.&lt;br /&gt;
* &#039;&#039;File name pattern (String)&#039;&#039; Filename patterns for the generated filename support the following tokens delimited by &amp;lt; and &amp;gt;: &lt;br /&gt;
** &amp;lt;code&amp;gt;&amp;lt;custom:paramname&amp;gt;&amp;lt;/code&amp;gt; is a means of automatically substituting in other parameters defined on the job.&lt;br /&gt;
** &amp;lt;code&amp;gt;&amp;lt;ext&amp;gt;&amp;lt;/code&amp;gt; is the last file extension (e.g. &#039;txt&#039;)&lt;br /&gt;
** &amp;lt;code&amp;gt;&amp;lt;ts:dateformat&amp;gt;&amp;lt;/code&amp;gt; corresponds to a timestamp formatted in the supplied SimpleDateFormat pattern (e.g. &amp;lt;code&amp;gt;&amp;lt;ts:yyyy-MM-dd-HH-mm:ss&amp;gt;&amp;lt;/code&amp;gt;)&lt;br /&gt;
** other literals outside of &amp;lt; or &amp;gt;, or inside of &amp;lt; and &amp;gt; but not matching any preceding tokens. &lt;br /&gt;
* &#039;&#039;CSV Delimiter (optional String)&#039;&#039; - If CSV export is selected, can choose an alternative delimiter other than comma (,). Single character.&lt;br /&gt;
* &#039;&#039;CSV Quote Character (optional String)&#039;&#039; - If CSV export is selected, can choose an alternative quote character other than double quotes (&amp;quot;). Single character.&lt;br /&gt;
* &#039;&#039;CSV Escape Character (optional String)&#039;&#039; - If CSV export is selected, can choose an alternative escape character other than double quotes (&amp;quot;). Single character.&lt;br /&gt;
* &#039;&#039;CSV Line End String (optional String)&#039;&#039; - If CSV export is selected, can choose an alternative line end string other than newline (\n).&lt;br /&gt;
* &#039;&#039;Datetime format (String)&#039;&#039; - SimpleDateFormat format applied to java.sql.Timestamps.&lt;br /&gt;
* &#039;&#039;Date format (String)&#039;&#039; - SimpleDateFormat format applied to java.sql.Dates.&lt;br /&gt;
&lt;br /&gt;
== REST Invocation Job ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.job.rest.RESTInvocationJob&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Introduced in Obsidian 5.1.0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This job calls a REST endpoint using the designated method and payload, if applicable. Expects the response code to be in the 2xx series to be a success, all others are treated as failures. Expects payload and response to always be Strings. Supports basic authentication. Response is saved to job results to allow for use by chained jobs.&lt;br /&gt;
&lt;br /&gt;
The job supports the following configuration options:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;URL Target (String)&#039;&#039; - Endpoint URL.&lt;br /&gt;
* &#039;&#039;REST API Method (String)&#039;&#039; - GET, PUT, POST, DELETE supported.&lt;br /&gt;
* &#039;&#039;Payload (optional String)&#039;&#039; - Required for PUT/POST, not permitted for GET/DELETE.&lt;br /&gt;
* &#039;&#039;Basic Authentication Username (optional String)&#039;&#039; - If wanting to add basic authentication, provide username and password.&lt;br /&gt;
* &#039;&#039;Basic Authentication Password (optional String)&#039;&#039; - If wanting to add basic authentication, provide username and password.&lt;br /&gt;
&lt;br /&gt;
== Obsidian Execution Statistics Job ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Job Class:&#039;&#039;&#039; &amp;lt;code&amp;gt;com.carfey.ops.job.schedule.ObsidianExecutionStatisticsJob&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Introduced in Obsidian 6.4.0&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This job runs against the job activity stored in Obsidian to collect execution statistics. These statistics are then available via the UI, the [[REST_API|REST API]] and [[Embedded_API|Embedded API]]. Job statistics use the data stored in job activity (history) and will not collect statistics for any history removed via [[Built-in_Jobs#Job_History_Cleanup_Job|Job History Cleanup Job]]. Likely you should align the &#039;&#039;maximumDurationLookback&#039;&#039; configuration value with a value that represents the most data kept. For example, if you keep 6 months of job history, you wouldn&#039;t benefit from a &#039;&#039;year&#039;&#039; lookback as the stats would match the &#039;&#039;six month&#039;&#039; stats.&lt;br /&gt;
&lt;br /&gt;
The job supports the following configuration options:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;durationUnit (String)&#039;&#039; - What statistics unit to calculate. Valid values: seconds, minutes, hours. Defaults to minutes. Multiple allowed.&lt;br /&gt;
* &#039;&#039;maximumDurationLookback (String)&#039;&#039; - How far back to collect statistics. One of day, two day, three day, five day, week, month, two month, three month, six month, year. Defaults to three month. Large durations will calculate and store stats for smaller durations; e.g. two day includes day.&lt;br /&gt;
&lt;br /&gt;
[[File:Obsidian-6.4.0-Obsidian-Execution-Job-Statistics-Config.png]]&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Authenticator&amp;diff=4138</id>
		<title>Authenticator</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Authenticator&amp;diff=4138"/>
		<updated>2026-07-31T14:49:47Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian ships with four built-in authenticators: native (database), LDAP, OAuth (OIDC), and SAML 2.0. A custom authenticator can also be implemented by extending the authenticator interface. The active authenticator is selected by the &#039;&#039;com.carfey.suite.security.Authenticator&#039;&#039; property in the Obsidian configuration file (&#039;&#039;com.carfey.properties&#039;&#039; or YAML equivalent).&lt;br /&gt;
&lt;br /&gt;
== OAuthAuthenticator (OIDC SSO) ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;OAuthAuthenticator&#039;&#039; enables Single Sign-On via any OIDC-compliant identity provider. It implements OAuth 2.0 Authorization Code + PKCE (S256). Full configuration reference: [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties|Advanced Configuration — OAuth / OIDC Authentication Properties]].&lt;br /&gt;
&lt;br /&gt;
=== How browser login works ===&lt;br /&gt;
&lt;br /&gt;
When &#039;&#039;OAuthAuthenticator&#039;&#039; is active, the Obsidian login page shows a single &amp;quot;Sign in with &amp;lt;displayName&amp;gt;&amp;quot; button instead of a username/password form. Clicking the button redirects the user to the identity provider&#039;s login page. After successful authentication at the IdP, the browser is redirected back to Obsidian with an authorization code. Obsidian exchanges the code for tokens at the IdP token endpoint and validates the id_token signature, issuer, audience, expiry, and nonce. &lt;br /&gt;
&lt;br /&gt;
No Obsidian database user record is created or required. Identity and roles come entirely from the validated token.&lt;br /&gt;
&lt;br /&gt;
=== User identity and display ===&lt;br /&gt;
&lt;br /&gt;
Obsidian uses the &#039;&#039;sub&#039;&#039; claim of the id_token as the stable internal user ID (stored in audit logs and session state). The &#039;&#039;sub&#039;&#039; claim is IdP-guaranteed unique and is never reassigned. A separate &#039;&#039;claim.displayName&#039;&#039;  property (default: &#039;&#039;preferred_username&#039;&#039;) controls what name appears in the Obsidian UI nav bar. Changing &#039;&#039;claim.displayName&#039;&#039; does not affect audit logging.&lt;br /&gt;
&lt;br /&gt;
=== Roles from IdP groups ===&lt;br /&gt;
&lt;br /&gt;
After the code exchange, Obsidian reads a configured groups claim from the id_token (or access token, or userinfo endpoint). Each group value is mapped to an Obsidian role via indexed &#039;&#039;role.&amp;lt;idx&amp;gt;.group&#039;&#039; / &#039;&#039;role.&amp;lt;idx&amp;gt;.roleName&#039;&#039; properties. At least one mapping to the &#039;&#039;Admin&#039;&#039; role is required. A user whose groups do not match any mapping authenticates successfully but has no access to role-gated actions (same behavior as a DB/LDAP user with no assigned roles).&lt;br /&gt;
&lt;br /&gt;
Valid Obsidian roles: &#039;&#039;&#039;Admin&#039;&#039;&#039;, &#039;&#039;&#039;API&#039;&#039;&#039;, &#039;&#039;&#039;Write&#039;&#039;&#039;, &#039;&#039;&#039;LimitedRead&#039;&#039;&#039;, &#039;&#039;&#039;Operator&#039;&#039;&#039;, &#039;&#039;&#039;Author&#039;&#039;&#039;, and folder-based roles following the &#039;&#039;&amp;lt;folder&amp;gt;-(Write|Author|Operator)&#039;&#039; convention (e.g. &#039;&#039;Maintenance-Author&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
=== REST API Bearer authentication ===&lt;br /&gt;
&lt;br /&gt;
REST clients can authenticate to Obsidian&#039;s REST API using a signed JWT access token in the &#039;&#039;Authorization: Bearer &amp;lt;token&amp;gt;&#039;&#039; header. Obsidian validates the token locally using cached JWKS keys. The userinfo endpoint is not called on the Bearer path. The client must have the &#039;&#039;API&#039;&#039; Obsidian role.&lt;br /&gt;
&lt;br /&gt;
=== Logout modes ===&lt;br /&gt;
&lt;br /&gt;
Two logout modes are supported: &lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;local&#039;&#039;&#039; (default) — Obsidian invalidates its own session. The IdP session remains active (useful for SSO environments where other applications share the IdP session).&lt;br /&gt;
* &#039;&#039;&#039;rp&#039;&#039;&#039; — RP-initiated logout. Obsidian redirects the browser to the IdP&#039;s &#039;&#039;end_session_endpoint&#039;&#039;, which terminates the IdP session. Requires the IdP to expose &#039;&#039;end_session_endpoint&#039;&#039; in its discovery document.&lt;br /&gt;
&lt;br /&gt;
=== v1 limitations ===&lt;br /&gt;
&lt;br /&gt;
* Opaque (non-JWT) access tokens are not supported on the REST/Bearer path.&lt;br /&gt;
* No &#039;&#039;jti&#039;&#039; replay protection (mitigate with short token lifetimes at the IdP).&lt;br /&gt;
* Microsoft Entra multi-tenant apps are not supported.&lt;br /&gt;
* No post-login IdP session validity check (mitigate with a short session timeout in &#039;&#039;web.xml&#039;&#039;).&lt;br /&gt;
* Dynamic role map (&#039;&#039;dynamicRole.prefix&#039;&#039;/&#039;&#039;suffix&#039;&#039;) requires a server restart when new root job folders are added.&lt;br /&gt;
* Installer UI supports up to 10 static group→role pairs; the runtime supports unlimited.&lt;br /&gt;
&lt;br /&gt;
== SAMLAuthenticator (SAML 2.0 SSO) ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;SAMLAuthenticator&#039;&#039; enables Single Sign-On via any SAML 2.0 identity provider, using Service Provider (SP) initiated browser flows. The class to configure is &#039;&#039;com.carfey.suite.security.saml.SAMLAuthenticator&#039;&#039;. Full configuration reference: [[Advanced_Configuration#SAML_Authentication_Properties|Advanced Configuration — SAML Authentication Properties]].&lt;br /&gt;
&lt;br /&gt;
=== How browser login works ===&lt;br /&gt;
&lt;br /&gt;
When &#039;&#039;SAMLAuthenticator&#039;&#039; is active, the Obsidian login page shows a single &amp;quot;Sign in with &amp;lt;displayName&amp;gt;&amp;quot; button instead of a username/password form. If &#039;&#039;displayName&#039;&#039; is left blank the button reads &amp;quot;Sign in with SSO&amp;quot;. Clicking it has Obsidian build a SAML &#039;&#039;AuthnRequest&#039;&#039; — signed with the SP keystore when &#039;&#039;signRequests&#039;&#039; is enabled — and redirect the browser to the identity provider using the HTTP-Redirect binding. After successful authentication at the IdP, the IdP posts a SAML response back to Obsidian&#039;s Assertion Consumer Service (ACS) endpoint.&lt;br /&gt;
&lt;br /&gt;
Obsidian validates the response status, the assertion signature against the IdP signing certificate published in the IdP&#039;s federation metadata, the issuer, the &#039;&#039;Destination&#039;&#039; and &#039;&#039;Recipient&#039;&#039; values, the &#039;&#039;AudienceRestriction&#039;&#039; against your SP entity ID, and the &#039;&#039;NotBefore&#039;&#039; / &#039;&#039;NotOnOrAfter&#039;&#039; time window (widened by &#039;&#039;clockSkewSeconds&#039;&#039;, default 60 seconds). On success Obsidian replaces the browser session with a new one and returns the user to the page originally requested, or to &#039;&#039;/&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
No Obsidian database user record is created or required. Identity and roles come entirely from the validated assertion.&lt;br /&gt;
&lt;br /&gt;
A failed callback invalidates the session and returns the browser to the login page. The underlying cause is written to the Obsidian log with a short correlation id that is not shown to the user.&lt;br /&gt;
&lt;br /&gt;
=== Service Provider endpoints ===&lt;br /&gt;
&lt;br /&gt;
All SAML endpoints live under &#039;&#039;/saml&#039;&#039; in the Obsidian web application:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;/saml/login&#039;&#039; — starts SP-initiated login and issues the &#039;&#039;AuthnRequest&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/saml/callback&#039;&#039; — the Assertion Consumer Service, receiving the IdP&#039;s &#039;&#039;SAMLResponse&#039;&#039; by HTTP-POST. Your &#039;&#039;assertionConsumerServiceUrl&#039;&#039; must point here.&lt;br /&gt;
* &#039;&#039;/saml/logout&#039;&#039; — starts logout from the Obsidian UI.&lt;br /&gt;
* &#039;&#039;/saml/slo&#039;&#039; — single logout endpoint, handling an IdP-initiated &#039;&#039;LogoutRequest&#039;&#039; and the IdP&#039;s &#039;&#039;LogoutResponse&#039;&#039;.&lt;br /&gt;
* &#039;&#039;/saml/metadata&#039;&#039; — serves Obsidian&#039;s SP metadata document for registration at the IdP.&lt;br /&gt;
&lt;br /&gt;
=== Identity provider metadata ===&lt;br /&gt;
&lt;br /&gt;
Obsidian reads IdP federation metadata from either &#039;&#039;idpMetadataUrl&#039;&#039; or &#039;&#039;idpMetadataFile&#039;&#039;, and caches it for &#039;&#039;metadata.ttlHours&#039;&#039; (default 24). Signing certificates come from that metadata, so key rotation at the IdP is picked up on the next refresh. If a refresh fails, Obsidian logs a warning and continues with the cached copy.&lt;br /&gt;
&lt;br /&gt;
=== User identity and display ===&lt;br /&gt;
&lt;br /&gt;
By default Obsidian uses the assertion&#039;s &#039;&#039;NameID&#039;&#039; as the stable internal user ID stored in audit logs and session state. Binding a different attribute with &#039;&#039;attribute.user&#039;&#039; is supported but logs a warning at startup, because a value the IdP may reassign risks one user inheriting another&#039;s history. A transient &#039;&#039;nameIdFormat&#039;&#039; also logs a warning: transient NameIDs are regenerated for each session, so every login appears as a new user. &#039;&#039;attribute.displayName&#039;&#039; optionally supplies the name shown in the Obsidian UI nav bar.&lt;br /&gt;
&lt;br /&gt;
=== Roles from SAML attributes ===&lt;br /&gt;
&lt;br /&gt;
Obsidian reads group memberships from the assertion attribute named by &#039;&#039;attribute.groups&#039;&#039;, or matched by &#039;&#039;attribute.groups.friendlyName&#039;&#039;. &#039;&#039;attribute.groups.valuePath&#039;&#039; and &#039;&#039;attribute.groups.stripPrefix&#039;&#039; help with structured or prefixed values such as directory distinguished names. Each group value is mapped to an Obsidian role via indexed &#039;&#039;role.&amp;lt;idx&amp;gt;.group&#039;&#039; / &#039;&#039;role.&amp;lt;idx&amp;gt;.roleName&#039;&#039; properties, exactly as for OAuth, and at least one mapping to the &#039;&#039;Admin&#039;&#039; role is required.&lt;br /&gt;
&lt;br /&gt;
By default (&#039;&#039;&#039;requireRole=true&#039;&#039;&#039;), a user whose groups match no role mapping cannot complete login. Setting &#039;&#039;requireRole=false&#039;&#039; lets such a user authenticate but with no access to role-gated actions, the same behavior as a DB or LDAP user with no assigned roles.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;dynamicRole.prefix&#039;&#039; and &#039;&#039;dynamicRole.suffix&#039;&#039; derive folder role mappings from your root job folders at startup, so adding a new root folder requires a restart before its roles resolve. Either &#039;&#039;attribute.groups&#039;&#039; or &#039;&#039;dynamicRole.prefix&#039;&#039; must be configured — with neither, every SAML user would resolve to zero roles, so the authenticator fails validation at startup.&lt;br /&gt;
&lt;br /&gt;
Valid Obsidian roles: &#039;&#039;&#039;Admin&#039;&#039;&#039;, &#039;&#039;&#039;API&#039;&#039;&#039;, &#039;&#039;&#039;Write&#039;&#039;&#039;, &#039;&#039;&#039;LimitedRead&#039;&#039;&#039;, &#039;&#039;&#039;Operator&#039;&#039;&#039;, &#039;&#039;&#039;Author&#039;&#039;&#039;, and folder-based roles following the &#039;&#039;&amp;lt;folder&amp;gt;-(Write|Author|Operator)&#039;&#039; convention (e.g. &#039;&#039;Maintenance-Author&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
=== Request signing and assertion trust ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;signRequests&#039;&#039; (default enabled) signs the outbound &#039;&#039;AuthnRequest&#039;&#039; and SP &#039;&#039;LogoutRequest&#039;&#039;, and requires &#039;&#039;keystore.path&#039;&#039;, &#039;&#039;keystore.password&#039;&#039; and &#039;&#039;keystore.alias&#039;&#039;. &#039;&#039;wantAssertionsSigned&#039;&#039; (default enabled) requires the IdP to sign assertions. Disabling both is accepted but logs a warning at startup, because assertions are then neither signed nor verified — do not do this in production.&lt;br /&gt;
&lt;br /&gt;
Encrypted assertions are &#039;&#039;&#039;not&#039;&#039;&#039; supported. The IdP must send signed, unencrypted assertions. Publish signing-capable key material in your SP metadata so that IdPs such as AD FS do not choose to encrypt.&lt;br /&gt;
&lt;br /&gt;
=== Logout modes ===&lt;br /&gt;
&lt;br /&gt;
Two logout modes are supported:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;local&#039;&#039;&#039; (default) — Obsidian invalidates its own session. The IdP session remains active.&lt;br /&gt;
* &#039;&#039;&#039;slo&#039;&#039;&#039; — SP-initiated single logout. Obsidian sends a signed &#039;&#039;LogoutRequest&#039;&#039; to the IdP&#039;s HTTP-Redirect &#039;&#039;SingleLogoutService&#039;&#039;. If the IdP metadata advertises no HTTP-Redirect SLO endpoint, Obsidian logs a warning and falls back to local logout. &#039;&#039;postLogoutRedirectUri&#039;&#039; sets the landing page after the IdP&#039;s &#039;&#039;LogoutResponse&#039;&#039;; when blank the browser returns to &#039;&#039;/&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
An IdP-initiated &#039;&#039;LogoutRequest&#039;&#039; posted to &#039;&#039;/saml/slo&#039;&#039; is honoured in either mode.&lt;br /&gt;
&lt;br /&gt;
=== No REST API access ===&lt;br /&gt;
&lt;br /&gt;
Obsidian&#039;s REST API is &#039;&#039;&#039;unavailable&#039;&#039;&#039; while &#039;&#039;SAMLAuthenticator&#039;&#039; is active. There is no SAML equivalent of the OAuth JWT Bearer path, and REST credential authentication is refused outright. Deployments that need REST access must run a separate Obsidian instance with an authenticator that supports it.&lt;br /&gt;
&lt;br /&gt;
=== v1 limitations ===&lt;br /&gt;
&lt;br /&gt;
* The REST API is not available while &#039;&#039;SAMLAuthenticator&#039;&#039; is active.&lt;br /&gt;
* SP-initiated login only. The callback requires an existing browser session, and a response carrying &#039;&#039;InResponseTo&#039;&#039; must match a pending &#039;&#039;AuthnRequest&#039;&#039; from that session, which is single-use and expires after five minutes.&lt;br /&gt;
* Encrypted assertions are not supported.&lt;br /&gt;
* Replay protection relies on that single-use &#039;&#039;AuthnRequest&#039;&#039; match plus the assertion time window; there is no persistent assertion ID store, so keep assertion lifetimes short at the IdP.&lt;br /&gt;
* Dynamic role map (&#039;&#039;dynamicRole.prefix&#039;&#039;/&#039;&#039;suffix&#039;&#039;) requires a server restart when new root job folders are added.&lt;br /&gt;
* Installer UI supports up to 10 static group→role pairs; the runtime supports unlimited.&lt;br /&gt;
&lt;br /&gt;
== Developing an Authenticator ==&lt;br /&gt;
Obsidian uses any valid implementation of the &amp;lt;code&amp;gt;com.carfey.suite.security.Authenticator&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;com.carfey.suite.security.remember.Authenticator&amp;lt;/code&amp;gt; interface. &lt;br /&gt;
&lt;br /&gt;
=== Implementation of a Custom Authenticator ===&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;com.carfey.suite.security.User authenticate(String username, String pass) throws com.carfey.suite.security.Authenticator.AuthenticationFailedException&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 6.1.1, you may optionally implement REST authentication distinctly. Defaults to standard authentication when not implemented.&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;com.carfey.suite.security.User authenticateREST(String username, String pass) throws com.carfey.suite.security.Authenticator.AuthenticationFailedException&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This method authenticates and returns a user with roles defined. Given a user name and a password either return a valid &amp;lt;code&amp;gt;com.carfey.suite.security.User&amp;lt;/code&amp;gt; object or throw a &amp;lt;code&amp;gt;com.carfey.suite.security.Authenticator.AuthenticationFailedException&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If authentication is successful, the &amp;lt;code&amp;gt;com.carfey.suite.security.User&amp;lt;/code&amp;gt; returned must have all its role memberships defined. This is done using the &amp;lt;code&amp;gt;com.carfey.suite.security.Role&amp;lt;/code&amp;gt; class. The assignment of &amp;lt;code&amp;gt;Role&amp;lt;/code&amp;gt;s to a &amp;lt;code&amp;gt;User&amp;lt;/code&amp;gt; can be done using any of the public constructors/setters or logical combination thereof defined below.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;code&amp;gt;public boolean supportsRememberMe()&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
From &amp;lt;code&amp;gt;com.carfey.suite.security.remember.Authenticator&amp;lt;/code&amp;gt;, available from Obsidian 3.5.0 and &#039;&#039;&#039;removed in Obsidian 7.0.0&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
This method indicated whether the Authenticator supported Obsidian&#039;s remember me feature in the Web Admin login. The Remember Me feature and the &amp;lt;code&amp;gt;com.carfey.suite.security.remember.Authenticator&amp;lt;/code&amp;gt; interface were removed in Obsidian 7.0.0, so this method is no longer part of the authenticator contract.&lt;br /&gt;
&lt;br /&gt;
=== Users ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public User(String userId)&lt;br /&gt;
&lt;br /&gt;
public User(String userId, Set&amp;lt;Role&amp;gt; roles, String firstName, String lastName, String email, boolean active*)&lt;br /&gt;
&lt;br /&gt;
public User(String userId, List&amp;lt;Role&amp;gt; roles, String firstName, String lastName, String email, boolean active*)&lt;br /&gt;
&lt;br /&gt;
public void setRoles(Set&amp;lt;Role&amp;gt; roles)&lt;br /&gt;
&lt;br /&gt;
public void setRoles(List&amp;lt;Role&amp;gt; roles)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&#039;&#039;* Note: If you wish to implement active user enabling/disabling, you must do so in your &amp;lt;code&amp;gt;Authenticator&amp;lt;/code&amp;gt; throwing &amp;lt;code&amp;gt;com.carfey.suite.security.Authenticator.AuthenticationFailedException&amp;lt;/code&amp;gt; when inactive users attempt to login.&lt;br /&gt;
&lt;br /&gt;
=== Roles ===&lt;br /&gt;
There are convenience constants that you should use in defining your role memberships. They can be found at &amp;lt;code&amp;gt;com.carfey.ops.Constant&amp;lt;/code&amp;gt;. The constants are &amp;lt;code&amp;gt;ADMIN_ROLE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;WRITE_ROLE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LIMITED_READ_ROLE&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;API_ROLE&amp;lt;/code&amp;gt;. As of &#039;&#039;5.0.0&#039;&#039;, you can also use &amp;lt;code&amp;gt;OPERATOR_ROLE&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AUTHOR_ROLE&amp;lt;/code&amp;gt;. &#039;&#039;Default&#039;&#039; rights are assumed for any authenticated user. Therefore, if someone authenticates that should not have access, throw a &amp;lt;code&amp;gt;com.carfey.suite.security.Authenticator.AuthenticationFailedException&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
When assigning the user&#039;s &amp;lt;code&amp;gt;com.carfey.suite.security.Role&amp;lt;/code&amp;gt;s, use the constructor &amp;lt;code&amp;gt;public Role(String roleId, String roleName)&amp;lt;/code&amp;gt; using the appropriate constant for both the &amp;lt;code&amp;gt;roleId&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;roleName&amp;lt;/code&amp;gt;. Role meanings are defined in [[Admin_User_Management#User_Rights | User Rights]].&lt;br /&gt;
&lt;br /&gt;
=== Putting it All Together ===&lt;br /&gt;
&lt;br /&gt;
Finance attempts to log in to Obsidian, gives valid credentials but should not be accessing Obsidian.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import static com.carfey.ops.Constant.*;&lt;br /&gt;
&lt;br /&gt;
import com.carfey.suite.security.Authenticator.AuthenticationFailedException;&lt;br /&gt;
import com.carfey.suite.security.Role;&lt;br /&gt;
import com.carfey.suite.security.User;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
public User authenticate(String username, String pass) throws AuthenticationFailedException {&lt;br /&gt;
    //usernameis &amp;quot;financeGuy&amp;quot;&lt;br /&gt;
    //pass is &amp;quot;mystrongpass&amp;quot;&lt;br /&gt;
    //credentials are valid, but user does not have any rights to Obsidian&lt;br /&gt;
&lt;br /&gt;
    throw new AuthenticationFailedException(String.format(&amp;quot;User [%s] is not authorized to use Obsidian Scheduler.&amp;quot;, username));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fred logs in using his username &#039;&#039;fredScheduler&#039;&#039; and his password &#039;&#039;badpass&#039;&#039;. You determine that his password is invalid.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import static com.carfey.ops.Constant.*;&lt;br /&gt;
&lt;br /&gt;
import com.carfey.suite.security.Authenticator.AuthenticationFailedException;&lt;br /&gt;
import com.carfey.suite.security.Role;&lt;br /&gt;
import com.carfey.suite.security.User;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
public User authenticate(String username, String pass) throws AuthenticationFailedException {&lt;br /&gt;
    //usernameis &amp;quot;fredScheduler&amp;quot;&lt;br /&gt;
    //pass is &amp;quot;badpass&amp;quot;&lt;br /&gt;
    //credentials are invalid&lt;br /&gt;
&lt;br /&gt;
    throw new AuthenticationFailedException(String.format(&amp;quot;User [%s] could not be authenticated.&amp;quot;, username));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fred logs in using his username &#039;&#039;fredScheduler&#039;&#039; and his password &#039;&#039;mystrongpassword&#039;&#039;. You determine that his password is valid and matches with the user and he has &#039;&#039;WRITE_ROLE&#039;&#039; rights.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public User authenticate(String username, String pass) throws AuthenticationFailedException {&lt;br /&gt;
    //usernameis &amp;quot;fredScheduler&amp;quot;&lt;br /&gt;
    //pass is &amp;quot;mystrongpassword&amp;quot;&lt;br /&gt;
    //credentials are valid, he has WRITE_ROLE&lt;br /&gt;
&lt;br /&gt;
    User user = new User(username);&lt;br /&gt;
    user.setRoles(Arrays.asList(new Role(WRITE_ROLE, WRITE_ROLE)));&lt;br /&gt;
    return user;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Tina logs in using her username &#039;&#039;tinaOperator&#039;&#039; and her password &#039;&#039;mystrongpassword&#039;&#039;. You determine that her password is valid and matches with the user and she has &#039;&#039;Default&#039;&#039; rights.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public User authenticate(String username, String pass) throws AuthenticationFailedException {&lt;br /&gt;
    //usernameis &amp;quot;tinaOperator&amp;quot;&lt;br /&gt;
    //pass is &amp;quot;mystrongpassword&amp;quot;&lt;br /&gt;
    //credentials are valid, Tina has default access&lt;br /&gt;
&lt;br /&gt;
    return new User(username);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The intern logs in using his username &#039;&#039;newGuy&#039;&#039; and his password &#039;&#039;mystrongpassword&#039;&#039;. You determine that his password is valid and matches with the user and he has &#039;&#039;LIMITED_READ_ROLE&#039;&#039; rights.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public User authenticate(String username, String pass) throws AuthenticationFailedException {&lt;br /&gt;
    //usernameis &amp;quot;newGuy&amp;quot;&lt;br /&gt;
    //pass is &amp;quot;mystrongpassword&amp;quot;&lt;br /&gt;
    //credentials are valid, the intern has LIMITED_READ_ROLE access&lt;br /&gt;
&lt;br /&gt;
    User user = new User(username);&lt;br /&gt;
    user.setRoles(Arrays.asList(new Role(LIMITED_READ_ROLE, LIMITED_READ_ROLE)));&lt;br /&gt;
    return user;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The new hire logs in using her username &#039;&#039;newHire&#039;&#039; and her password is &#039;&#039;i am a new hire&#039;&#039;. You determine that her password is valid and matches with the user and she has &#039;&#039;OPERATOR_ROLE&#039;&#039; rights only for the job folder roots &#039;&#039;DevOps&#039;&#039; and &#039;&#039;FinOps&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
 private static final String DEV_OPS_OPERATOR = &amp;quot;DevOps-&amp;quot; + OPERATOR_ROLE;&lt;br /&gt;
 private static final String FIN_OPS_OPERATOR = &amp;quot;FinOps-&amp;quot; + OPERATOR_ROLE;&lt;br /&gt;
 public User authenticate(String username, String pass) throws AuthenticationFailedException {&lt;br /&gt;
     //usernameis &amp;quot;newHire&amp;quot;&lt;br /&gt;
     //pass is &amp;quot;i am a new hire&amp;quot;&lt;br /&gt;
     //credentials are valid, the new hire has OPERATOR_ROLE access for job folder roots &#039;&#039;DevOps&#039;&#039; and &#039;&#039;FinOps&#039;&#039;&lt;br /&gt;
 &lt;br /&gt;
    User user = new User(username);&lt;br /&gt;
    user.setRoles(Arrays.asList(new Role(DEV_OPS_OPERATOR, DEV_OPS_OPERATOR), new Role(FIN_OPS_OPERATOR, FIN_OPS_OPERATOR)));&lt;br /&gt;
    return user;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Customizing our LDAP Authenticator ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;com.carfey.suite.security.LdapAuthenticator&amp;lt;/code&amp;gt; is Obsidian&#039;s LDAP Authentication class that, when combined with its configurability that is documented [[Advanced_Configuration#Configuration_Details|here]], meets most needs. But with the large variety of LDAP servers and potential implementations of &#039;&#039;dn&#039;&#039; strings, role and group definition and membership and even authentication methods supported, you may need to tweak its use somewhat. Rather than require you to write your own LDAP Authenticator, we have made efforts to make ours flexible enough to be specialized.&lt;br /&gt;
&lt;br /&gt;
=== Specialization points ===&lt;br /&gt;
====&amp;lt;code&amp;gt;protected void buildUpContextEnvironment(String pass, String dn, Hashtable&amp;lt;String, String&amp;gt; environment)&amp;lt;/code&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
Stores needed environment attributes for authentication using &amp;lt;code&amp;gt;javax.naming.directory.InitialDirContext&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Currently stores:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Context.INITIAL_CONTEXT_FACTORY, &amp;quot;com.sun.jndi.ldap.LdapCtxFactory&amp;quot;&lt;br /&gt;
Context.PROVIDER_URL, {{ldap_url}} &lt;br /&gt;
Context.SECURITY_AUTHENTICATION, &amp;quot;simple&amp;quot; //or overridden securityAuthentication by configuration com.carfey.suite.security.LdapAuthenticator.securityAuthentication &lt;br /&gt;
Context.SECURITY_PRINCIPAL, {{user_dn}} &lt;br /&gt;
Context.SECURITY_CREDENTIALS, {{user_pass}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;lt;code&amp;gt;protected boolean isMemberOfGroup(DirContext authContext, String groupName, String dn) throws NamingException&amp;lt;/code&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
Checks the authenticated &amp;lt;code&amp;gt;DirContext&amp;lt;/code&amp;gt; for group membership. Used to determine &#039;&#039;Default&#039;&#039; rights to Obsidian, in addition to its defined rights [[Admin_User_Management#User_Rights|roles]]. Currently queries &amp;lt;code&amp;gt;&amp;quot;uniquemember&amp;quot;&amp;lt;/code&amp;gt;,&amp;lt;code&amp;gt;&amp;quot;uniqueMember&amp;quot;&amp;lt;/code&amp;gt;,&amp;lt;code&amp;gt;&amp;quot;member&amp;quot;&amp;lt;/code&amp;gt;,&amp;lt;code&amp;gt;&amp;quot;roleOccupant&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;memberOf&amp;quot;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&amp;quot;MemberOf&amp;quot;&amp;lt;/code&amp;gt; attributes for any match on the &#039;&#039;dn&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Deploying your Authenticator ==&lt;br /&gt;
Once your authenticator class is written, you need to deploy the compiled class and any dependent libraries to your admin web application instances as JAR files. These should be placed under the &amp;lt;code&amp;gt;/WEB-INF/lib&amp;lt;/code&amp;gt; directory of either the Obsidian web application directory or WAR file.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Advanced_Configuration&amp;diff=4137</id>
		<title>Advanced Configuration</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Advanced_Configuration&amp;diff=4137"/>
		<updated>2026-07-31T14:49:46Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian requires some initial configuration parameters for database connection information and desired authentication mechanisms.  The installer takes care of configuring these as provided.  For your reference should you wish to manually create/edit these, note the details below.&lt;br /&gt;
&lt;br /&gt;
== Configuration Details ==&lt;br /&gt;
&lt;br /&gt;
The following sections show the available properties and sample values that can be used in the &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; properties file. While properties format is shown, all the same configuration items apply to yaml support. See [[#Properties.2FYaml_File|Properties/Yaml File]] for more details on this file.&lt;br /&gt;
&lt;br /&gt;
=== Authentication Properties ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Comment out the native authenticator setup&lt;br /&gt;
#com.carfey.suite.security.Authenticator=com.carfey.suite.security.DBAuthenticator&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LDAP Authentication Properties ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#Set your LDAP info here&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.LdapAuthenticator&lt;br /&gt;
# You can specify the attribute type used in building up the distinguished name (dn). If unspecified, defaults to cn&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.dn.attribute=uid&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of dn.bases to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.dn.base=ou=people,o=MyOrgHere&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.url=ldap://localhost:10389&lt;br /&gt;
# You can specify the SECURITY_AUTHENTICATION. Defaults to simple.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.securityAuthentication=simple&lt;br /&gt;
# Any necessary additional information such as Provider, Principal and Host will need to be handled in your custom class extending LdapAuthenticator.&lt;br /&gt;
&lt;br /&gt;
# You can login via an LDAP attribute that is not part of the dn. A matching entry is found in the LDAP Directory (using anonymous or a fixed lookup account)&lt;br /&gt;
# after which the dn attribute above is retrieved to perform the user&#039;s authentication. The following 5 attributes are only used for this type of lookup and authentication.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.lookupDnAttribute=false&lt;br /&gt;
# This is the attribute name that is being searched for in the LDAP directory to build the dn for eventual authentication.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.loginAttribute=sAMAccountName&lt;br /&gt;
# This determines if these lookups will be done anonymously (without a session authenticated by user/password)&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.anonymousEnabled=false&lt;br /&gt;
# If anonymous lookups are not permitted or desired, provide the dn and password to be used for lookups. Typically, a read-only account with&lt;br /&gt;
# read rights to the LDAP entries and attributes in question is sufficient.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchUserFullDn=cn=ObsidianLDAPReadOnly,ou=people,o=MyOrgHere&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchPassword=password&lt;br /&gt;
# As of Obsidian 4.5.1, an alternate search base configuration parameter is used for the searchUser&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchUserDnBase=ou=people,o=MyOrgHere&lt;br /&gt;
# As of Obsidian 4.7.1, you may specify one or more additional group membership attributes beyond the defaults&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.groupMembershipAttribute=businessGroup~~~departmentName&lt;br /&gt;
&lt;br /&gt;
# Configure who may access the web app, based on LDAP group membership&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.accessDN=cn=SchedulerAccess,ou=groups,o=MyOrgHere&lt;br /&gt;
&lt;br /&gt;
# Configure the Write role used in the admin web app, based on LDAP group membership (by default, users may only read)&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.write.dn=cn=SchedulerWrite,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.write.roleName=Write&lt;br /&gt;
&lt;br /&gt;
# Configure the Admin role used in the admin web app, based on LDAP group membership (users may configure system parameters, etc).&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.admin.dn=cn=SchedulerAdmin,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.admin.roleName=Admin&lt;br /&gt;
&lt;br /&gt;
# Configure the LimitedRead role used in the admin web app, based on LDAP group membership (by default, users may only read)&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.limitedRead.dn=cn=SchedulerLimitedRead,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.limitedRead.roleName=LimitedRead&lt;br /&gt;
&lt;br /&gt;
# Configure the APIrole used by the REST API, based on LDAP group membership&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.api.dn=cn=SchedulerAPI,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.api.roleName=API&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OAuth / OIDC Authentication Properties ====&lt;br /&gt;
&lt;br /&gt;
To use OAuth / OIDC SSO, set the &#039;&#039;Authenticator&#039;&#039; property to&lt;br /&gt;
&#039;&#039;com.carfey.suite.security.OAuthAuthenticator&#039;&#039; and add the properties below. All&lt;br /&gt;
properties are prefixed with &#039;&#039;com.carfey.suite.security.OAuthAuthenticator.&#039;&#039; (shown&lt;br /&gt;
without prefix in the table for brevity).&lt;br /&gt;
&lt;br /&gt;
===== Required properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;issuer&#039;&#039; || OIDC Issuer URL. Obsidian fetches &#039;&#039;&amp;lt;issuer&amp;gt;/.well-known/openid-configuration&#039;&#039; lazily on first use and caches it.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clientId&#039;&#039; || Client ID registered at the IdP.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clientSecret&#039;&#039; || Client secret. Keep out of version control.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;redirectUri&#039;&#039; || Exact callback URL registered at the IdP (e.g. &#039;&#039;https://app.example.com/oauth/callback&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;scopes&#039;&#039; || Space-separated OAuth scopes (e.g. &#039;&#039;openid profile email groups&#039;&#039;). Must include &#039;&#039;openid&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groups&#039;&#039; || Name of the token claim that carries group memberships (e.g. &#039;&#039;groups&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.group&#039;&#039; || IdP group value for this mapping. &#039;&#039;&amp;lt;idx&amp;gt;&#039;&#039; is an arbitrary stable label (numeric or descriptive).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.roleName&#039;&#039; || Obsidian role for this mapping. At least one &#039;&#039;Admin&#039;&#039; mapping is required. Valid roles: &#039;&#039;&#039;Admin&#039;&#039;&#039;, &#039;&#039;&#039;API&#039;&#039;&#039;, &#039;&#039;&#039;Write&#039;&#039;&#039;, &#039;&#039;&#039;LimitedRead&#039;&#039;&#039;, &#039;&#039;&#039;Operator&#039;&#039;&#039;, &#039;&#039;&#039;Author&#039;&#039;&#039;, and folder-based roles (&#039;&#039;&amp;lt;folder&amp;gt;-(Write|Author|Operator)&#039;&#039;).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Optional properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Default !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;displayName&#039;&#039; || (none) || Label on the SSO button: &amp;quot;Sign in with &#039;&#039;&amp;lt;displayName&amp;gt;&#039;&#039;&amp;quot;. Defaults to &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;logout.mode&#039;&#039; || &#039;&#039;local&#039;&#039; || &#039;&#039;local&#039;&#039;: invalidates Obsidian session only. &#039;&#039;rp&#039;&#039;: RP-initiated logout via IdP &#039;&#039;end_session_endpoint&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;postLogoutRedirectUri&#039;&#039; || (none) || Where to redirect after RP-initiated logout. Ignored when &#039;&#039;logout.mode=local&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.user&#039;&#039; || &#039;&#039;sub&#039;&#039; || Claim used as the internal user ID in audit logs. &#039;&#039;&#039;Strongly recommend keeping as &#039;&#039;sub&#039;&#039;&#039;&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.displayName&#039;&#039; || &#039;&#039;preferred_username&#039;&#039; || Claim shown in the Obsidian UI nav bar. Separate from &#039;&#039;claim.user&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.validateTyp&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, rejects Bearer tokens where &#039;&#039;typ&#039;&#039; is present but not &#039;&#039;at+JWT&#039;&#039;. Set &#039;&#039;false&#039;&#039; only for legacy IdPs that omit &#039;&#039;typ&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clockSkewSeconds&#039;&#039; || &#039;&#039;60&#039;&#039; || Clock skew tolerance for &#039;&#039;exp&#039;&#039;/&#039;&#039;nbf&#039;&#039; validation (seconds).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;metadata.ttlHours&#039;&#039; || &#039;&#039;24&#039;&#039; || OIDC discovery document cache TTL in hours.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;expectedAccessTokenAudience&#039;&#039; || &#039;&#039;clientId&#039;&#039; || Expected &#039;&#039;aud&#039;&#039; for Bearer access tokens. See security note below.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsSource&#039;&#039; || &#039;&#039;idtoken&#039;&#039; || Where to read groups during browser SSO: &#039;&#039;idtoken&#039;&#039;, &#039;&#039;accesstoken&#039;&#039;, or &#039;&#039;userinfo&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsBearerSource&#039;&#039; || &#039;&#039;accesstoken&#039;&#039; || Where to read groups on the Bearer path: &#039;&#039;idtoken&#039;&#039; or &#039;&#039;accesstoken&#039;&#039;. &#039;&#039;&#039;userinfo is not permitted on the Bearer path.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsValuePath&#039;&#039; || (none) || Sub-field name when groups is an array of objects (e.g. &#039;&#039;name&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsStripPrefix&#039;&#039; || (none) || Prefix to strip from each group value (e.g. &#039;&#039;/&#039;&#039; for Keycloak full-path style).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsUserinfoTimeout&#039;&#039; || &#039;&#039;5&#039;&#039; || HTTP timeout in seconds for the userinfo endpoint. Only used when &#039;&#039;claim.groupsSource=userinfo&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.prefix&#039;&#039; || (none) || Prefix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.suffix&#039;&#039;. Map is rebuilt at startup; adding a new root folder requires a restart.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.suffix&#039;&#039; || (none) || Suffix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.prefix&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;requireRole&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, browser SSO fails if IdP groups map to no Obsidian roles. Does not apply to REST Bearer tokens.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Security notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;expectedAccessTokenAudience and cross-JWT confusion&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When &#039;&#039;expectedAccessTokenAudience&#039;&#039; is not set it defaults to &#039;&#039;clientId&#039;&#039;. In many OIDC&lt;br /&gt;
configurations the id_token also carries &#039;&#039;aud=clientId&#039;&#039;, which could allow an id_token&lt;br /&gt;
to be submitted as a Bearer token. The &#039;&#039;claim.validateTyp=true&#039;&#039; default mitigates this&lt;br /&gt;
for compliant IdPs (access tokens carry &#039;&#039;typ=at+JWT&#039;&#039;; id_tokens carry &#039;&#039;typ=JWT&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
For maximum defence, set &#039;&#039;expectedAccessTokenAudience&#039;&#039; to a distinct resource-server&lt;br /&gt;
identifier if your IdP supports it.&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;claim.validateTyp=false&#039;&#039; AND &#039;&#039;expectedAccessTokenAudience&#039;&#039; is not explicitly set,&lt;br /&gt;
Obsidian logs a mandatory startup WARN advising you to set &#039;&#039;expectedAccessTokenAudience&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
===== Keycloak example =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.OAuthAuthenticator&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.issuer=http://localhost:8081/realms/obsidian&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.clientId=obsidian-client&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.clientSecret=test-client-secret-local&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.redirectUri=http://localhost:8080/oauth/callback&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.scopes=openid profile email groups&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.claim.groups=groups&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.claim.groupsSource=idtoken&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.1.group=Obsidian-Admins&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.1.roleName=Admin&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.2.group=Obsidian-Write&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.2.roleName=Write&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.3.group=Obsidian-Read&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.3.roleName=LimitedRead&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.4.group=Obsidian-API&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.4.roleName=API&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.displayName=Keycloak&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.logout.mode=local&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.requireRole=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Provider notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Microsoft Entra ID&#039;&#039;&#039;: groups claim contains GUIDs by default; configure Optional&lt;br /&gt;
Claims for display names. Accounts with 200+ groups trigger overage. Multi-tenant apps not&lt;br /&gt;
supported in v1.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auth0&#039;&#039;&#039;: opaque access tokens (Auth0 default) are not supported on the Bearer path.&lt;br /&gt;
Configure a custom API audience to get JWT access tokens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Okta&#039;&#039;&#039;: use the Custom Authorization Server URL, not the Org Authorization Server.&lt;br /&gt;
&lt;br /&gt;
===== v1 limitations =====&lt;br /&gt;
&lt;br /&gt;
* Opaque access tokens not supported on the REST/Bearer path.&lt;br /&gt;
* No &#039;&#039;jti&#039;&#039; replay protection.&lt;br /&gt;
* Microsoft Entra multi-tenant apps not supported.&lt;br /&gt;
* No post-login IdP session validity check.&lt;br /&gt;
* Dynamic role map requires restart for new root folders.&lt;br /&gt;
* Installer UI supports up to 10 static role pairs; runtime supports unlimited.&lt;br /&gt;
&lt;br /&gt;
==== SAML Authentication Properties ====&lt;br /&gt;
&lt;br /&gt;
To use SAML 2.0 SSO, set the &#039;&#039;Authenticator&#039;&#039; property to&lt;br /&gt;
&#039;&#039;com.carfey.suite.security.saml.SAMLAuthenticator&#039;&#039; and add the properties below. All&lt;br /&gt;
properties are prefixed with &#039;&#039;com.carfey.suite.security.saml.SAMLAuthenticator.&#039;&#039; (shown&lt;br /&gt;
without prefix in the table for brevity). Note the &#039;&#039;.saml.&#039;&#039; package segment in both values.&lt;br /&gt;
&lt;br /&gt;
Obsidian acts as a SAML Service Provider and supports SP-initiated browser login only. See&lt;br /&gt;
[[Authenticator#SAMLAuthenticator_(SAML_2.0_SSO)|Authenticator — SAMLAuthenticator]] for how the&lt;br /&gt;
login and logout flows behave and for the list of &#039;&#039;/saml&#039;&#039; endpoints.&lt;br /&gt;
&lt;br /&gt;
===== Required properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;entityId&#039;&#039; || SP entity ID registered at the IdP (e.g. &#039;&#039;https://app.example.com/obsidian&#039;&#039;). Obsidian also requires this value to appear in the assertion&#039;s &#039;&#039;AudienceRestriction&#039;&#039; when the IdP sends one.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;assertionConsumerServiceUrl&#039;&#039; || Exact ACS URL where the IdP posts responses. This must be Obsidian&#039;s callback endpoint, e.g. &#039;&#039;https://app.example.com/obsidian/saml/callback&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;idpMetadataUrl&#039;&#039; &#039;&#039;&#039;or&#039;&#039;&#039; &#039;&#039;idpMetadataFile&#039;&#039; || Source of the IdP federation metadata: an HTTP(S) URL, or an absolute path to a local XML file. Set exactly one of the two.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups&#039;&#039; || Name of the assertion attribute carrying group memberships (e.g. &#039;&#039;groups&#039;&#039;). Required unless &#039;&#039;dynamicRole.prefix&#039;&#039; is set instead.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.group&#039;&#039; || IdP group value for this mapping. &#039;&#039;&amp;lt;idx&amp;gt;&#039;&#039; is an arbitrary stable label (numeric or descriptive). Not needed if all roles come from &#039;&#039;dynamicRole&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.roleName&#039;&#039; || Obsidian role for this mapping. The installer requires at least one &#039;&#039;Admin&#039;&#039; mapping, and you need one in practice so that somebody can administer Obsidian. Valid roles: &#039;&#039;&#039;Admin&#039;&#039;&#039;, &#039;&#039;&#039;Write&#039;&#039;&#039;, &#039;&#039;&#039;LimitedRead&#039;&#039;&#039;, &#039;&#039;&#039;Operator&#039;&#039;&#039;, &#039;&#039;&#039;Author&#039;&#039;&#039;, and folder-based roles (&#039;&#039;&amp;lt;folder&amp;gt;-(Write|Author|Operator)&#039;&#039;). The &#039;&#039;API&#039;&#039; role has no effect on a SAML install, because the REST API is unavailable.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Because &#039;&#039;signRequests&#039;&#039; defaults to enabled, a default configuration also requires the three&lt;br /&gt;
&#039;&#039;keystore&#039;&#039; properties below. Disable &#039;&#039;signRequests&#039;&#039; only if your IdP does not require signed&lt;br /&gt;
authentication requests.&lt;br /&gt;
&lt;br /&gt;
===== Optional properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Default !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;displayName&#039;&#039; || (none) || Label on the SSO button: &amp;quot;Sign in with &#039;&#039;&amp;lt;displayName&amp;gt;&#039;&#039;&amp;quot;. Defaults to &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;signRequests&#039;&#039; || &#039;&#039;true&#039;&#039; || Signs the outbound &#039;&#039;AuthnRequest&#039;&#039; and SP &#039;&#039;LogoutRequest&#039;&#039;. Requires the three &#039;&#039;keystore&#039;&#039; properties.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.path&#039;&#039; || (none) || Path to the keystore holding the SP signing key. Required when &#039;&#039;signRequests&#039;&#039; is enabled.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.password&#039;&#039; || (none) || Keystore password. Keep out of version control.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.alias&#039;&#039; || (none) || Alias of the SP signing key within the keystore.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;wantAssertionsSigned&#039;&#039; || &#039;&#039;true&#039;&#039; || Requires the IdP to sign assertions, verified against the signing certificate in the IdP metadata.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;singleLogoutServiceUrl&#039;&#039; || (none) || SP single logout URL. When set it is published in Obsidian&#039;s SP metadata and is matched against the &#039;&#039;Destination&#039;&#039; of an incoming &#039;&#039;LogoutRequest&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;logout.mode&#039;&#039; || &#039;&#039;local&#039;&#039; || &#039;&#039;local&#039;&#039;: invalidates the Obsidian session only. &#039;&#039;slo&#039;&#039;: SP-initiated single logout via the IdP&#039;s HTTP-Redirect &#039;&#039;SingleLogoutService&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;postLogoutRedirectUri&#039;&#039; || (none) || Where to redirect after the IdP&#039;s &#039;&#039;LogoutResponse&#039;&#039;. When blank the browser returns to &#039;&#039;/&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;nameIdFormat&#039;&#039; || &#039;&#039;urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified&#039;&#039; || &#039;&#039;NameIDPolicy&#039;&#039; format requested from the IdP. A transient format logs a startup warning, because transient NameIDs cannot serve as stable user IDs.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.user&#039;&#039; || &#039;&#039;nameId&#039;&#039; || Assertion attribute used as the internal user ID in audit logs. &#039;&#039;&#039;Strongly recommend keeping as &#039;&#039;nameId&#039;&#039;&#039;&#039;&#039;; any other value logs a startup warning.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.displayName&#039;&#039; || (none) || Attribute shown in the Obsidian UI nav bar. Separate from &#039;&#039;attribute.user&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.friendlyName&#039;&#039; || (none) || Match the groups attribute on its &#039;&#039;FriendlyName&#039;&#039; instead of its &#039;&#039;Name&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.valuePath&#039;&#039; || (none) || Sub-field name when the groups attribute holds structured values.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.stripPrefix&#039;&#039; || (none) || Prefix to strip from each group value (e.g. &#039;&#039;/&#039;&#039; for Keycloak full-path style).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;metadata.ttlHours&#039;&#039; || &#039;&#039;24&#039;&#039; || IdP federation metadata cache TTL in hours. A failed refresh falls back to the cached copy with a warning.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clockSkewSeconds&#039;&#039; || &#039;&#039;60&#039;&#039; || Clock skew tolerance for &#039;&#039;NotBefore&#039;&#039; / &#039;&#039;NotOnOrAfter&#039;&#039; validation (seconds).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;forceAuthn&#039;&#039; || &#039;&#039;false&#039;&#039; || Sets &#039;&#039;ForceAuthn=&amp;quot;true&amp;quot;&#039;&#039; on the &#039;&#039;AuthnRequest&#039;&#039;, asking the IdP to reauthenticate the user.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;authnContextClassRef&#039;&#039; || (none) || Requests a specific &#039;&#039;RequestedAuthnContext&#039;&#039;. A differing context in the response is logged as a warning, not rejected.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.prefix&#039;&#039; || (none) || Prefix for auto-generated folder-role group mappings. Usable on its own. The map is built at startup, so adding a new root job folder requires a restart.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.suffix&#039;&#039; || (none) || Optional suffix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.prefix&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;requireRole&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, browser SSO fails if the user&#039;s group attributes map to no Obsidian roles.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Security notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Encrypted assertions are not supported&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Obsidian requires signed, unencrypted assertions. If your SP metadata carries no signing key&lt;br /&gt;
material, Obsidian logs a startup warning: IdPs such as AD FS may then choose to encrypt the&lt;br /&gt;
assertion, which this release cannot process. Configure the SP keystore and register the&lt;br /&gt;
generated metadata so the IdP sees a signing &#039;&#039;KeyDescriptor&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Do not disable both signature settings&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Setting &#039;&#039;signRequests&#039;&#039; and &#039;&#039;wantAssertionsSigned&#039;&#039; both to &#039;&#039;false&#039;&#039; is accepted but logs a&lt;br /&gt;
startup warning. In that state assertions are neither signed nor verified, which is not safe for&lt;br /&gt;
production.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SP-initiated flows and replay&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Obsidian issues an &#039;&#039;AuthnRequest&#039;&#039; and tracks its ID in the browser session for five minutes.&lt;br /&gt;
When the IdP&#039;s response carries &#039;&#039;InResponseTo&#039;&#039;, it must match one of those pending IDs and is&lt;br /&gt;
consumed on use, so the same response cannot be replayed. There is no persistent store of&lt;br /&gt;
processed assertion IDs, so keep assertion lifetimes short at the IdP.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Clustered deployments and IdP-initiated logout&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
An IdP-initiated &#039;&#039;LogoutRequest&#039;&#039; can only end the Obsidian session on the node that receives&lt;br /&gt;
it. Use sticky sessions if you rely on single logout across a cluster.&lt;br /&gt;
&lt;br /&gt;
===== Keycloak example =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.saml.SAMLAuthenticator&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.entityId=https://app.example.com/obsidian&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.assertionConsumerServiceUrl=https://app.example.com/obsidian/saml/callback&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.idpMetadataUrl=https://keycloak.example.com/realms/obsidian/protocol/saml/descriptor&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.path=/opt/obsidian/saml-sp.p12&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.password=changeit&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.alias=obsidian-sp&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.attribute.groups=groups&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.1.group=Obsidian-Admins&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.1.roleName=Admin&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.2.group=Obsidian-Write&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.2.roleName=Write&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.3.group=Obsidian-Read&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.3.roleName=LimitedRead&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.displayName=Keycloak&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.logout.mode=local&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.requireRole=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Provider notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Microsoft Entra ID&#039;&#039;&#039;: group claims are omitted from SAML assertions once an account belongs to&lt;br /&gt;
roughly 150 groups. Filter the groups sent to the application, or map app roles instead. Note this&lt;br /&gt;
threshold differs from the OIDC one described above.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AD FS&#039;&#039;&#039;: the federation metadata document is normally at&lt;br /&gt;
&#039;&#039;/FederationMetadata/2007-06/FederationMetadata.xml&#039;&#039;. Register signing-capable SP metadata so AD FS&lt;br /&gt;
does not encrypt the assertion, which Obsidian cannot decrypt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Okta&#039;&#039;&#039;: use the application&#039;s SAML metadata URL for &#039;&#039;idpMetadataUrl&#039;&#039;. Okta&#039;s logout endpoints&lt;br /&gt;
may be POST-only; if no HTTP-Redirect &#039;&#039;SingleLogoutService&#039;&#039; is advertised, &#039;&#039;logout.mode=slo&#039;&#039;&lt;br /&gt;
falls back to local logout.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auth0&#039;&#039;&#039;: federation metadata is served at &#039;&#039;samlp/metadata/&amp;lt;client-id&amp;gt;&#039;&#039;. Group memberships must&lt;br /&gt;
be added to the assertion with an Auth0 Action or rule.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Keycloak and other generic IdPs&#039;&#039;&#039;: Keycloak publishes a SAML descriptor at&lt;br /&gt;
&#039;&#039;/realms/&amp;lt;realm&amp;gt;/protocol/saml/descriptor&#039;&#039;. Any IdP that publishes SAML 2.0 metadata with an&lt;br /&gt;
HTTP-POST &#039;&#039;AssertionConsumerService&#039;&#039; target and a signing certificate will work.&lt;br /&gt;
&lt;br /&gt;
===== v1 limitations =====&lt;br /&gt;
&lt;br /&gt;
* The REST API is unavailable while &#039;&#039;SAMLAuthenticator&#039;&#039; is active — there is no Bearer or JWT equivalent.&lt;br /&gt;
* SP-initiated login only.&lt;br /&gt;
* Encrypted assertions are not supported.&lt;br /&gt;
* No persistent assertion replay store beyond the single-use &#039;&#039;InResponseTo&#039;&#039; match and the assertion time window.&lt;br /&gt;
* Dynamic role map (&#039;&#039;dynamicRole.prefix&#039;&#039;/&#039;&#039;suffix&#039;&#039;) requires a server restart when new root job folders are added.&lt;br /&gt;
* Installer UI supports up to 10 static role pairs; the runtime supports unlimited.&lt;br /&gt;
&lt;br /&gt;
=== SMTP Mail Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Email configuration for notifications, if desired&lt;br /&gt;
#for straight up open relay, just specify the host using&lt;br /&gt;
mail.smtp.host=smtp.myopenrelayhost.com&lt;br /&gt;
mail.smtp.port=port &lt;br /&gt;
#(standard ports are 25, 465 for SSL, 587 for TLS)&lt;br /&gt;
&lt;br /&gt;
#for using TLS and SSL, provide these as necessary&lt;br /&gt;
mail.smtp.socketFactory.port=port&lt;br /&gt;
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory&lt;br /&gt;
mail.smtp.auth=true&lt;br /&gt;
mail.smtp.user=myauthenticatedemailuser@email.com&lt;br /&gt;
mail.smtp.password=PASSWORD&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# We support JNDI lookups for email sessions. All other email properties can be excluded.&lt;br /&gt;
mail.session.jndi.path=java:comp/env/mail/session&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Database Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Database configuration&lt;br /&gt;
com.carfey.obsidian.db.url=jdbc:mysql://localhost/obsidian&lt;br /&gt;
com.carfey.obsidian.db.userId=user&lt;br /&gt;
com.carfey.obsidian.db.password=pass&lt;br /&gt;
&lt;br /&gt;
# Sample JDBC URL formats for all platforms&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:mysql://localhost:3306/obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:h2:C:/dev/workspace/obsidian;MVCC=TRUE&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:oracle:thin:@localhost:1521:obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:postgresql://localhost:5432/obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:sqlserver://localhost:1433;databaseName=obsidian&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 6.3.0, we support constructing simple JDBC urls from component properties&lt;br /&gt;
# Only supports basic URL formats noted above and does not support H2&lt;br /&gt;
#com.carfey.obsidian.db.host=localhost&lt;br /&gt;
#com.carfey.obsidian.db.port=3306&lt;br /&gt;
#com.carfey.obsidian.db.databaseName=obsidianDB&lt;br /&gt;
#com.carfey.obsidian.db.dbType=mariadb/mysql/oracle/postgresql/sqlserver&lt;br /&gt;
#com.carfey.obsidian.db.oracleSid=SIDORCL&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
# We support JNDI lookups for database connectivity.&lt;br /&gt;
com.carfey.obsidian.db.url=java:comp/env/jdbc/obsidian&lt;br /&gt;
com.carfey.obsidian.db.jndiType=mysql&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=mysql is also used for MariaDB&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=oracle&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=postgresql&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=h2&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=sqlserver&lt;br /&gt;
&lt;br /&gt;
# Table prefix may be used to add a prefix to tables and related database objects. It must be 6 or fewer characters and can contain letters or underscores.&lt;br /&gt;
com.carfey.obsidian.db.tablePrefix=OBSDN_&lt;br /&gt;
&lt;br /&gt;
# For Oracle databases, when Obsidian&#039;s tables exist in a different schema from the user specified above, or if the user does not default to the schema matching its name, specify the target schema here.&lt;br /&gt;
# For details on the required privileges to run with an alternate user, see https://wiki.obsidianscheduler.com/docs/Obsidian_Tables#Oracle_Privileges&lt;br /&gt;
# This same configuration item can be used to configure the Postgresql schema. If unspecified, the default is public.&lt;br /&gt;
# If using JNDI and Oracle/Postgresql, we recommend you set this value to ensure best performance.&lt;br /&gt;
com.carfey.obsidian.db.schema=obsidian&lt;br /&gt;
&lt;br /&gt;
#default max connections per pool if not specified is 50&lt;br /&gt;
com.carfey.obsidian.db.maxConnections=40&lt;br /&gt;
&lt;br /&gt;
#default millis timeout to retrieve available connection from pool if not specified is 2000&lt;br /&gt;
com.carfey.obsidian.db.connectionTimeout=2000&lt;br /&gt;
&lt;br /&gt;
# You can disable pooling of connections, which is suggested only if using another connection pool through JNDI. When true, maxConnections is ignored.&lt;br /&gt;
# When a JNDI data source is selected in the installer, this is set to true by default.&lt;br /&gt;
com.carfey.obsidian.db.disablePooling=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.3.0, Oracle database supports configured wait time.&lt;br /&gt;
com.carfey.obsidian.db.timedLockWaitEnabled=true (default)&lt;br /&gt;
com.carfey.obsidian.db.timedLockWaitSeconds=1 (default, prior to 4.3.0, indefinite wait)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Logging Properties ===&lt;br /&gt;
As of &#039;&#039;&#039;Obsidian 5.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Please see [https://logging.apache.org/log4j/2.x/manual/configuration.html Log4j2 configuration]&lt;br /&gt;
&lt;br /&gt;
Before &#039;&#039;&#039;Obsidian 5.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Please see [https://logging.apache.org/log4j/1.2/manual.html Log4j Configuration]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Miscellaneous Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# If you wish to change the tokens used to reference global parameters in your job configuration, use the following two values:&lt;br /&gt;
global.param.start.token={{&lt;br /&gt;
global.param.end.token=}}&lt;br /&gt;
&lt;br /&gt;
# Populated by the installer, these values can be used to initialize licence key information into the database.&lt;br /&gt;
com.carfey.obsidian.licence.key=licenceKey&lt;br /&gt;
# Name is only required for hardware-linked or site licenses. Use the name exactly as provided by Carfey Software Corporation in your license email.&lt;br /&gt;
com.carfey.obsidian.licence.name=licenceeName&lt;br /&gt;
&lt;br /&gt;
# If you have issues with classloading (e.g. in Grails or Spring), this flag enables usage of the context classloader .&lt;br /&gt;
com.carfey.jdk.useContextClassLoader=true&lt;br /&gt;
&lt;br /&gt;
# You can set the scheduler host designator name multiple times in the same JVM by using this parameter:&lt;br /&gt;
com.carfey.obsidian.schedulerDesignation=obsidian-dev&lt;br /&gt;
&lt;br /&gt;
# Job Forking can be enabled and configured per node&lt;br /&gt;
com.carfey.obsidian.jvmJobForkingEnabledOnThisNode=true&lt;br /&gt;
# This property is the location of the fork scripts. Obsidian is bundled with obsidianForkedJob.bat and obsidianForkedJob.sh. These are the expected script names.&lt;br /&gt;
com.carfey.obsidian.forkedJobScriptLocation=/Obsidian-3.0.0&lt;br /&gt;
# Use the following optional property if you need to override the default classpath that is built using the contents of the standalone directory.&lt;br /&gt;
# This allows for job forking support in embedded and even webapp deployments. Use the classpath format supported by your operating system.&lt;br /&gt;
#com.carfey.obsidian.forkedJobscriptClasspathOverride=&lt;br /&gt;
# Use the following optional property if you wish to include database configuration parameters as arguments passed to the forking scripts and the target forked Obsidian class.&lt;br /&gt;
# For example, you may need to do this if your configuration is done via System Property overrides or programmmatically in your embedded Obsidian instance.&lt;br /&gt;
#com.carfey.obsidian.jvmJobForkingIncludeDbSysParms=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.3.0, description/parameter formatting is supported. Custom formatter (com.carfey.ops.job.config.formatter.Formatter) supported via this parameter.&lt;br /&gt;
com.carfey.obsidian.formatterClass=com.carfey.ops.job.config.formatter.MarkdownFormatter (default)&lt;br /&gt;
&lt;br /&gt;
# As of 4.4.0, support for running DDL outside Obsidian and applying only the data portion of upgrades. May require incremental upgrades. Contact support for assistance. Enabled with the following:&lt;br /&gt;
com.carfey.obsidian.runner.skipDDL=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.5.0, Slack notifications can be automatically configured for FATAL, ERROR and WARN events by setting this property with a Slack incoming web hook URL. The user name property is optional and defaults to Obsidian Scheduler&lt;br /&gt;
com.carfey.obsidian.slack.webhookUrl=https://hooks.slack.com/services/XXXXXXX/YYYYYYYY/ZZZZZZZZZZ&lt;br /&gt;
com.carfey.obsidian.slack.userName=Obsidian Scheduler (prod)&lt;br /&gt;
&lt;br /&gt;
# As of 4.6.0, Obsidian supports starting a node in paused state. This can also be set as a System property.&lt;br /&gt;
com.carfey.obsidian.schedulerPausedOnStartup=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.10.0, Obsidian supports disabling script engine jobs selectively.&lt;br /&gt;
com.carfey.ops.job.script.BeanShellJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.GroovyJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.RubyJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.PythonJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.JavaScriptJob.enabled=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.0.0, Signal interrupt (supported as of 4.5.1) is disabled by default. Use configuration to enable.&lt;br /&gt;
com.carfey.obsidian.signalHandlerActiveForScheduler=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.0, you can send Obsidian events to a REST endpoint via an out-of-the-box Event Hook. The basicAuthorization property is optional, the sample showing its use for myusername:mypassword. The &amp;quot;Basic &amp;quot; prefix is added by Obsidian.&lt;br /&gt;
com.carfey.obsidian.restfulEventHook.url=https://myendpoint.com:1234/rest/obsidian_events&lt;br /&gt;
com.carfey.obsidian.restfulEventHook.basicAuthorization=bXl1c2VybmFtZTpteXBhc3N3b3Jk&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.1, maintenance jobs are automatically scheduled for new installations. Set the following property to disable this behaviour.&lt;br /&gt;
com.carfey.obsidian.skipMaintenanceJobInitialization=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.1, you can send Obsidian events to standard output and error streams using an out-of-the-box Event Hook. You can enable the default behaviour and auto-register it by adding the following configuration.&lt;br /&gt;
com.carfey.obsidian.standardOutputStreamsEventHook.enabled=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Table Prefixes ===&lt;br /&gt;
As shown in full configuration reference, a table name prefix may be specified to create Obsidian tables with names beginning with a specified string.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Table prefix may be used to add a prefix to tables and related database objects. It must be 6 or fewer characters and can contain letters or underscores.&lt;br /&gt;
com.carfey.obsidian.db.tablePrefix=OBSDN_&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Properties/Yaml File ==&lt;br /&gt;
&lt;br /&gt;
The above configuration must reside in a properties file named &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; or a yaml file named &amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; found according to the rules of [http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String) ClassLoader.getResource].  If both yaml and properties files are found, yaml is loaded first and properties values will override. Some possibilities include &amp;lt;code&amp;gt;WEB-INF/classes/com.carfey.(properties/yaml)&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;com.carfey.(properties/yaml)&amp;lt;/code&amp;gt; file in a directory which is explicitly added to the classpath, or at the root of a jar file as is done in &amp;lt;code&amp;gt;obsidian-props.jar&amp;lt;/code&amp;gt; for standalone deployments.&lt;br /&gt;
&lt;br /&gt;
Instead of using a classpath resource for properties, you can specify an external properties file using the Java system property &amp;lt;code&amp;gt;carfey.properties.file&amp;lt;/code&amp;gt;. All expected properties must be specified in either the default &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; file on the classpath or in the override file. Any properties found in both files will use the override file&#039;s values. Usage: &amp;lt;code&amp;gt;-Dcarfey.properties.file=/home/obsidian/obsidian.properties&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Instead of using a classpath resource for yaml, you can specify an external yaml file using the Java system property &amp;lt;code&amp;gt;carfey.yaml.file&amp;lt;/code&amp;gt;. All expected configuration values must be specified in either the default &amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; file on the classpath or in the override file. Any configuration values found in both files will use the override file&#039;s values. Usage: &amp;lt;code&amp;gt;-Dcarfey.yaml.file=/home/obsidian/obsidian.yaml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You may also use a programmatic properties override. Simply call &amp;lt;code&amp;gt;com.carfey.jdk.sys.Configurator.setOverride(Properties props)&amp;lt;/code&amp;gt; as early as possible in the application startup.  To quote the javadoc of this method, &#039;&#039;For use as a programmatic properties override. This must be called before any classes are accessed either through invocation or class initialization that may require access to configuration done through properties. Best if done as early as possible in code, perhaps first in an entry point class that does little else and then hands-off to existing entry point.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 4.4, you may specify configuration values as System Properties or Environment Variables. Duplicated values are resolved as follows:&lt;br /&gt;
# System Property&lt;br /&gt;
# Environment Variable - &#039;&#039;&#039;&#039;&#039;overridden by System Property&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
# Properties - &#039;&#039;&#039;&#039;&#039;overridden by Environment Variable and System Property&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Notifications Configuration ==&lt;br /&gt;
&lt;br /&gt;
Sending notifications requires SMTP configuration to be defined in the Obsidian properties file. The properties file reference at the beginning of this page includes SMTP configuration details.&lt;br /&gt;
&lt;br /&gt;
In addition to the properties file, you can configure some Notifications settings to tweak how your emails are sent. These values are configurable under the &#039;&#039;Dispatch&#039;&#039; category of the [[Admin Scheduler Settings|scheduler settings]] screen. Defaults are provided, but you can update them appropriately for your needs.  &lt;br /&gt;
&lt;br /&gt;
=== Notification sender address and DMARC ===&lt;br /&gt;
&lt;br /&gt;
The notificationFromEmail scheduler setting controls the From address used for notification emails. It is available under:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Scheduler Settings → Dispatch → notificationFromEmail&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Set this to an address belonging to your organization and authorized by the SMTP service configured for your Obsidian installation.&lt;br /&gt;
&lt;br /&gt;
Do not use ops-notification@obsidianscheduler.com when sending through your own SMTP infrastructure. Your SMTP server normally cannot authenticate mail for the obsidianscheduler.com domain. This can cause SPF, DKIM, and DMARC failures and may result in notifications being quarantined or rejected.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
scheduler-notifications@example.com&lt;br /&gt;
&lt;br /&gt;
After updating the value, trigger a test notification and confirm successful receipt.&lt;br /&gt;
&lt;br /&gt;
[[File:NotifSettings_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
== Dependent Libraries ==&lt;br /&gt;
&lt;br /&gt;
Obsidian Scheduler requires a number of third party libraries, both for the web administration application and the scheduler itself.  Below is information on these libraries and how they are used.  Unless otherwise noted, they are mandatory. Our installer supports selective [[Installation_Guide#Configuring_3rd_Party_Library_Conflict_Management|conflict management]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Authentication (OAuth / OIDC)&#039;&#039;&lt;br /&gt;
All libraries in this section are only required when OAuthAuthenticator is active. &#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* oauth2-oidc-sdk-11.23.1.jar, nimbus-jose-jwt-10.0.2.jar. Nimbus OAuth 2.0 SDK with OpenID Connect extensions and JOSE+JWT support.&lt;br /&gt;
* content-type-2.3.jar, lang-tag-1.7.jar. Supporting libraries for the Nimbus SDK.&lt;br /&gt;
* json-smart-2.5.2.jar, accessors-smart-2.5.2.jar. JSON parsing for the Nimbus SDK.&lt;br /&gt;
* jcip-annotations-1.0-1.jar. Concurrency annotations for the Nimbus SDK.&lt;br /&gt;
* asm-9.7.1.jar. Bytecode manipulation library; transitive dependency of the Nimbus SDK.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Authentication (SAML 2.0)&#039;&#039;&lt;br /&gt;
All libraries in this section are only required when SAMLAuthenticator is active; the installer removes them when SAML is not the selected authentication type. &#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* opensaml-core-4.3.2.jar, opensaml-saml-api-4.3.2.jar, opensaml-saml-impl-4.3.2.jar, opensaml-security-api-4.3.2.jar, opensaml-security-impl-4.3.2.jar, opensaml-xmlsec-api-4.3.2.jar, opensaml-xmlsec-impl-4.3.2.jar, opensaml-messaging-api-4.3.2.jar, opensaml-profile-api-4.3.2.jar, opensaml-soap-api-4.3.2.jar, opensaml-soap-impl-4.3.2.jar, opensaml-storage-api-4.3.2.jar. OpenSAML 4 modules implementing SAML 2.0 messages, metadata and signature handling.&lt;br /&gt;
* java-support-8.4.2.jar. Shibboleth utility library required by OpenSAML.&lt;br /&gt;
* xmlsec-2.3.4.jar. Apache Santuario XML Signature implementation.&lt;br /&gt;
* cryptacular-1.2.5.jar, bcprov-jdk18on-1.85.jar, bcpkix-jdk18on-1.85.jar, bcutil-jdk18on-1.85.jar. Cryptographic providers used for assertion signature verification and SP key handling.&lt;br /&gt;
* velocity-engine-core-2.3.jar. Template engine used by OpenSAML&#039;s message encoders.&lt;br /&gt;
* commons-lang3-3.20.0.jar, commons-codec-1.15.jar. Apache Commons utilities required by OpenSAML.&lt;br /&gt;
* httpclient-4.5.14.jar, httpcore-4.4.16.jar. HTTP client used to retrieve IdP federation metadata.&lt;br /&gt;
* guava-32.1.3-jre.jar, failureaccess-1.0.1.jar, listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar. Guava collections and its companion placeholder artifacts, required by OpenSAML.&lt;br /&gt;
* metrics-core-4.2.25.jar. Dropwizard Metrics, required by OpenSAML.&lt;br /&gt;
* slf4j-api-2.0.12.jar, log4j-slf4j2-impl-2.26.0.jar. SLF4J API and its log4j2 binding, so OpenSAML&#039;s diagnostic logging is routed into Obsidian&#039;s log4j2 configuration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Embedded Tomcat (Obsidian Embedded Tomcat JAR packs only)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* tomcat-embed-core-10.1.39.jar, tomcat-embed-el-10.1.39.jar — Apache Tomcat embedded components (Apache License 2.0). Present in &#039;&#039;&#039;embed-tomcat-lib/&#039;&#039;&#039; when an Embedded Tomcat pack is selected.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Common&#039;&#039;&lt;br /&gt;
* obsidian.jar.  Core Obsidian lib.&lt;br /&gt;
* gson-2.10.1. Supports REST API and web administration.&lt;br /&gt;
* jmustache-1.15.jar. Provides email templating.&lt;br /&gt;
* log4j-api-2.26.0.jar, log4j-core-2.26.0.jar.  Obsidian logging uses log4j2.&lt;br /&gt;
* snakeyaml-2.3.jar. Support for YAML configuration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Databases&#039;&#039;&lt;br /&gt;
* h2-1.4.200.jar.  H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* mariadb-java-client-2.7.2.jar. MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* mssql-jdbc-9.2.1.jre11.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc11-21.1.0.0.jar. Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-42.2.19.jar.  PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Mail&#039;&#039; &lt;br /&gt;
* activation-1.1.jar, javax.mail-1.6.2.jar. Used for javamail (javax) email notifications.&lt;br /&gt;
* angus-activation-2.0.1.jar, angus-mail-2.0.2.jar. Optionally used with Jakarta EE email notifications.&lt;br /&gt;
* jakarta.activation-api-2.1.2.jar, jakarta.mail-api-2.1.2.jar. Used for Jakarta EE email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Scripts&#039;&#039;&lt;br /&gt;
* bsh-2.0b5.jar.  Support for our Bean Shell script execution. (http://www.beanshell.org).  Only required for BeanShellJob usage.&lt;br /&gt;
* jruby-9.2.17.0.jar. Support for Ruby script execution (https://www.jruby.org/). Only required for RubyJob usage.&lt;br /&gt;
* jython-standalone-2.7.2.jar. Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Scripts - Prior to Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Groovy scripting shipped as a single uber archive: apache-groovy-all-4.0.24.jar&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Scripts - As of Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Groovy scripting ships as 13 modular JARs: groovy-4.0.24.jar, groovy-astbuilder-4.0.24.jar, groovy-datetime-4.0.24.jar, groovy-dateutil-4.0.24.jar, groovy-json-4.0.24.jar, groovy-jsr223-4.0.24.jar, groovy-macro-4.0.24.jar, groovy-macro-library-4.0.24.jar, groovy-nio-4.0.24.jar, groovy-sql-4.0.24.jar, groovy-templates-4.0.24.jar, groovy-typecheckers-4.0.24.jar, groovy-xml-4.0.24.jar.&lt;br /&gt;
Plus four owned transitives (included when Groovy is enabled): asm-analysis-9.7.1.jar, asm-tree-9.7.1.jar, asm-util-9.7.1.jar, javaparser-core-3.26.2.jar&lt;br /&gt;
All 17 JARs above are excluded when Groovy scripting is disabled at install time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Web Admin&#039;&#039;&lt;br /&gt;
* dom4j-2.1.3.jar.  XML utilities.&lt;br /&gt;
* flexmark-0.62.2.jar, flexmark-util-0.62.2.jar. Markdown formatting for Description and Parameter job annotations.&lt;br /&gt;
* jxl-2.6.12.jar. Excel file format utilities. Only required in web administration.&lt;br /&gt;
* opencsv-5.4.jar. Support for CSV downloads in UI.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Web Admin - As of Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Markdown rendering for job and parameter descriptions ships as 12 modular JARs:&lt;br /&gt;
flexmark-0.62.2.jar, flexmark-util-ast-0.62.2.jar, flexmark-util-builder-0.62.2.jar, flexmark-util-collection-0.62.2.jar, flexmark-util-data-0.62.2.jar, flexmark-util-dependency-0.62.2.jar, flexmark-util-format-0.62.2.jar, flexmark-util-html-0.62.2.jar, flexmark-util-misc-0.62.2.jar, flexmark-util-options-0.62.2.jar, flexmark-util-sequence-0.62.2.jar, flexmark-util-visitor-0.62.2.jar&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;, the following changes were made:&lt;br /&gt;
* &#039;&#039;&#039;log4j upgraded&#039;&#039;&#039;: log4j-api and log4j-core updated from 2.17.1 to 2.26.0.&lt;br /&gt;
* &#039;&#039;&#039;Modular Groovy libraries&#039;&#039;&#039;: Obsidian no longer ships &#039;&#039;&#039;apache-groovy-all-4.0.24.jar&#039;&#039;&#039;. Groovy scripting now uses 13 modular JARs (see &#039;&#039;Scripts&#039;&#039; above). When Groovy is disabled at install time, all 13 modules are excluded.&lt;br /&gt;
* &#039;&#039;&#039;Modular Flexmark libraries&#039;&#039;&#039;: Obsidian no longer ships &#039;&#039;&#039;flexmark-util-0.62.2.jar&#039;&#039;&#039;. Markdown rendering for job and parameter descriptions now uses 12 modular Flexmark JARs (see &#039;&#039;Web Admin&#039;&#039; above).&lt;br /&gt;
* &#039;&#039;&#039;OAuth / OIDC authentication libraries&#039;&#039;&#039; added (Nimbus OAuth 2.0 SDK and transitive dependencies). Required only when OAuthAuthenticator is configured. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties|Authentication (OAuth/OIDC)]] above.&lt;br /&gt;
* &#039;&#039;&#039;SAML 2.0 authentication libraries&#039;&#039;&#039; added (OpenSAML 4 and its transitive dependencies, plus the SLF4J-to-log4j2 binding). Required only when SAMLAuthenticator is configured. See [[Advanced_Configuration#SAML_Authentication_Properties|Authentication (SAML 2.0)]] above.&lt;br /&gt;
* Embedded Tomcat runtime added (tomcat-embed-core-10.1.39.jar, tomcat-embed-el-10.1.39.jar). Present only when an Embedded Tomcat pack is selected. Replaces the Jetty quick-start distribution which has been removed.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 7.0.0&#039;&#039;&#039;, the following libraries were in use.&lt;br /&gt;
* log4j-api-2.17.1.jar, log4j-core-2.17.1.jar.  Obsidian logging uses log4j2.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 6.0.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* groovy-all-3.0.7.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.4.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* gson-2.8.6.jar. Supports REST API and web administration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.2.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* jakarta.activation-1.2.1.jar. Used for email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.0.3&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* &#039;&#039;Obsidian 5.0.2&#039;&#039; log4j-api-2.16.0.jar, log4j-core-2.16.0.jar.  &lt;br /&gt;
* &#039;&#039;Obsidian 5.0.1&#039;&#039; log4j-api-2.15.0.jar, log4j-core-2.15.0.jar.  &lt;br /&gt;
* &#039;&#039;Obsidian 5.0.0&#039;&#039; log4j-api-2.14.1.jar, log4j-core-2.14.1.jar.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.0.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* activation-1.1.jar, javax.mail-1.5.5.jar.  Used for email notifications.&lt;br /&gt;
* dom4j-1.6.1.jar. XML utilities.&lt;br /&gt;
* gson-2.7.jar. Supports REST API and web administration.&lt;br /&gt;
* bsh-2.0b4.jar.  Support for our Bean Shell script execution. (http://www.beanshell.org).  Only required for BeanShellJob usage.&lt;br /&gt;
* groovy-all-2.4.14.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
* jython-standalone-2.7.0.jar. Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
* jruby-complete-9.2.7.0.jar. Support for Ruby script execution (https://www.jruby.org/). Only required for RubyJob usage.&lt;br /&gt;
* jmustache-1.12.jar. Provides email templating.&lt;br /&gt;
* h2-1.4.192.  H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* log4j-1.2.9.jar.  Obsidian logging uses log4j.&lt;br /&gt;
* mariadb-java-client-1.4.5.jar.  MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* jtds-1.3.1.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc7-12.1.0.1.jar.  Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-9.4.1208.jre7.jar. PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
* opencsv-3.8.jar. Support for CSV downloads in UI.&lt;br /&gt;
* flexmark-0.19.6.jar, flexmark-util-0.19.6.jar (as of 4.3.0). Markdown formatting for Description and Parameter job annotations.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 4.5.1&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* groovy-all-2.4.7.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 4.0.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* javax.mail-1.5.2.jar.  Used for email notifications.&lt;br /&gt;
* jstl.jar, standard.jar.  Web utilities. Only required in web administration.&lt;br /&gt;
* gson-2.2.2.jar. Supports REST API and web administration.&lt;br /&gt;
* groovy-all-2.1.8.jar.  Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
* jython-standalone-2.5.3.jar.  Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
* jmustache-1.8.jar. Provides email templating.&lt;br /&gt;
* h2-1.3.154. H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* mariadb-java-client-1.1.5.jar. MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* jtds-1.2.8.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc6-11.2.0.3.jar. Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-9.0-801.jdbc4.jar.  PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.9.1&#039;&#039;&#039;, the following redundant library was in included in the Obsidian packaging.&lt;br /&gt;
* smtp.jar.  Used for email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.2.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* groovy-all-1.7.6.jar&lt;br /&gt;
* jython.jar (version 2.5.2rc2)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.1.1&#039;&#039;&#039;, the following libraries were also included.&lt;br /&gt;
* carfey-date-1.2.jar or carfey-date-1.1.jar.  Date math/manipulation.&lt;br /&gt;
* jdk-gen.jar, jdk.jar, suite-gen.jar, suite.jar.  Core Obsidian libs.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.1.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* ojdbc14.jar Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 1.5&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* gson-1.5.jar. Supports REST API and web administration.&lt;br /&gt;
&lt;br /&gt;
=== Required Libraries for Embedded API ===&lt;br /&gt;
&lt;br /&gt;
To use the [[Embedded_API|Embedded API]], the following resources should be imported. If you have newer versions of the same JARs in your application, feel free to use the newer version. The JAR files below can generally be found in the &#039;&#039;standalone&#039;&#039; directory if your installation. The &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; file can be extracted from the &amp;lt;code&amp;gt;obsidian-properties-configuration.jar&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;obsidian-yaml-configuration.jar&amp;lt;/code&amp;gt; in the &#039;&#039;standalone&#039;&#039; directory, or from an Obsidian WAR file under &amp;lt;code&amp;gt;WEB-INF/classes&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* com.carfey.properties/com.carfey.yaml (configuration file), or obsidian-properties-configuration.jar/obsidian-yaml-configuration.jar, which contains the configuration file&lt;br /&gt;
* obsidian.jar&lt;br /&gt;
* dom4j-2.1.3.jar&lt;br /&gt;
* flexmark-0.62.2.jar plus the modular Flexmark JARs listed under &#039;&#039;Web Admin&#039;&#039; above&lt;br /&gt;
* gson-2.10.1&lt;br /&gt;
* log4j-api-2.26.0.jar, log4j-core-2.26.0.jar&lt;br /&gt;
* Appropriate JDBC JAR (e.g. mariadb-java-client-2.7.2.jar for MySQL or MariaDB)&lt;br /&gt;
&lt;br /&gt;
In addition, the following resources should be included if you wish to have notification support enabled when using the API:&lt;br /&gt;
* jmustache-1.15.jar&lt;br /&gt;
** jakarta.activation-api-2.1.2.jar, jakarta.mail-api-2.1.2.jar. Used for Jakarta EE email notifications.&lt;br /&gt;
** angus-activation-2.0.1.jar, angus-mail-2.0.2.jar. Optionally used with Jakarta EE email notifications.&lt;br /&gt;
** activation-1.1.jar, javax.mail-1.6.2.jar. Used for javamail (javax) email notifications.&lt;br /&gt;
&lt;br /&gt;
Finally, if you need to configure scripting jobs from the API, you will need to include any applicable JARs from the following list:&lt;br /&gt;
* apache-groovy-all-4.0.24.jar&lt;br /&gt;
* bsh-2.0b5.jar&lt;br /&gt;
* jruby-9.2.17.0.jar&lt;br /&gt;
* jython-standalone-2.7.2.jar&lt;br /&gt;
&lt;br /&gt;
== Disabling Job Execution &amp;amp; Scheduling in the Web Application ==&lt;br /&gt;
See the instructions within the [[Getting_Started#Disabling_Job_Scheduling_in_the_Web_Application|Getting Started Guide]].&lt;br /&gt;
&lt;br /&gt;
== Disabling Automatic Database Updates ==&lt;br /&gt;
See the instructions within the [[Getting_Started#Disabling_Automatic_Database_Updates|Getting Started Guide]].&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Admin_Web_Application_Guide&amp;diff=4136</id>
		<title>Admin Web Application Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Admin_Web_Application_Guide&amp;diff=4136"/>
		<updated>2026-07-31T14:49:45Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;Important: This guide is for Obsidian 4.0 and newer versions.&lt;br /&gt;
&lt;br /&gt;
Obsidian&#039;s administration web application lets you manage scheduler settings, monitor job execution, create and update jobs, and manage various notification settings. &lt;br /&gt;
&lt;br /&gt;
It uses a sidebar on the left hand side of each page for primary navigation. This sidebar has parent menu items which are expanded by clicking on them. The sidebar can be hidden via the arrow at the bottom of the menu, or the hamburger icon on the right side of the screen.&lt;br /&gt;
&lt;br /&gt;
It supports modern browsers including Chrome, Firefox, Edge, and more. It is a responsive mobile application, so you can use it on your phone or tablet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Though Obsidian is fully functional in other browsers, we recommend you use browsers such as Chrome or Firefox since they provide a better user experience and vastly superior performance.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:ObsidianNav_4.0.png|400px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To set up the web application, see &#039;&#039;&#039;[[Getting Started]]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Screens available in Obsidian&#039;s web application are discussed on the following pages:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;[[Admin Login|Login]]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Admin Job Activity|Job Activity]]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Admin Jobs|Jobs]]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Admin Job Stats|Job Stats]]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Admin Global Parameters|Job Global Parameters]]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Admin Schedule Aliases|Schedule Aliases]]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Admin Job Run Time Preview|Job Run Time Preview]]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Admin Job Conflicts|Job Conflicts]]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Admin Job Chains|Job Chains]]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Admin Custom Calendars|Calendars]]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Admin Logs|Logs]]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Admin Notifications|Sent Notifications]]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Admin Subscribers|Subscribers ]]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Admin Templates|Email Templates]]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Admin Scheduler Settings|Scheduler Settings]]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Admin Host Status|Host Status]]&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Admin User Management|User Management (including MFA)]]&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Admin_User_Management&amp;diff=4135</id>
		<title>Admin User Management</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Admin_User_Management&amp;diff=4135"/>
		<updated>2026-07-31T14:49:45Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;User management can be accessed by &#039;&#039;Admin&#039;&#039; users from the primary navigation sidebar. The current user may view their rights and/or change their password by using the user name drop-down in the top right corner of the screen.&lt;br /&gt;
&lt;br /&gt;
All users may see their current rights. In addition, if native authentication is used, all users may change their password. &#039;&#039;Admin&#039;&#039; users may also view and manage users when native authentication is used.&lt;br /&gt;
&lt;br /&gt;
== Viewing Rights &amp;amp; Changing Your Password ==&lt;br /&gt;
&lt;br /&gt;
Selecting &#039;&#039;My User&#039;&#039; from the user name dropdown in the top right of the screen will bring you to the page which lists your user rights. If native authentication is used, you may also change your password from this page.&lt;br /&gt;
&lt;br /&gt;
[[Image:UserChangePassword_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:UserChangePasswordFull_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
== Listing Users ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Admin&#039;&#039; users may view current users, add users, and update settings and passwords of existing users. &lt;br /&gt;
&lt;br /&gt;
[[Image:UserListing_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Exporting Results ===&lt;br /&gt;
The current contents of the table can be exported to Excel, CSV or XML by clicking on export icon displayed at the far right of the table header. The download will automatically begin and will include all pages of the current table of results.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Changing or Adding Users ==&lt;br /&gt;
&lt;br /&gt;
Clicking the (+) button in the table header will bring you to the New User screen. Clicking a user in the table will allow you to edit or view a user. For security reasons, you must always enter your current user password to make user changes.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The question mark icon that appears next to the form title indicates inline help. Click on the icon to see help related to the current page.&lt;br /&gt;
&lt;br /&gt;
[[Image:AddUserSave_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When saving changes, you may receive error notifications. In this case, no changes were saved and you may correct your user setup and resubmit. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; When editing existing users, you may change their password by entering the new password and confirmation. If you leave both blank, the password will remain unchanged.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:AddUserEnterPassword_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
== Deleting Users ==&lt;br /&gt;
&lt;br /&gt;
You can also delete users from the edit page. For security reasons, you must always enter your current user password before deleting a user. You will be prompted to confirm the delete action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Deleting users can make tracking down who made changes more difficult. Consider making a user inactive instead.&lt;br /&gt;
&lt;br /&gt;
[[Image:DeleteUser_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
== User Rights ==&lt;br /&gt;
&lt;br /&gt;
Rights are broken out into these categories: &#039;&#039;Default&#039;&#039; (read), &#039;&#039;Write&#039;&#039;, &#039;&#039;Admin&#039;&#039;, &#039;&#039;API  Access&#039;&#039;, &#039;&#039;Author&#039;&#039; and &#039;&#039;Operator&#039;&#039;. In addition, &#039;&#039;Default&#039;&#039; read access can be limited using the &#039;&#039;Limited Read Access&#039;&#039; setting.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Default&#039;&#039; rights allow you read access to most parts of the application, with the exception of user lists and the [[Admin Scheduler Settings|Scheduler Settings]] screen.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Write&#039;&#039; rights allow you perform most operations, with the exception of viewing and changing users, and the [[Admin Scheduler Settings|Scheduler Settings]] screen.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Admin&#039;&#039; rights allow you access to view and change [[Admin Scheduler Settings|Scheduler Settings]], pause/resume [[Admin Host Status|scheduler hosts]] and manage users.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;API  Access&#039;&#039; grants the user the ability to use the full [[REST API]]. In addition, this role grants the &#039;&#039;Default&#039;&#039; read access to the admin application.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Limited Read Access&#039;&#039; prevents the user from seeing potentially sensitive job configuration parameters. If you wish to hide these values from a user, grant them this right, otherwise leave it unselected. Note that if a user has Write or API Access, this setting has no effect.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Author&#039;&#039; rights allow you to create and modify jobs, job chains and conflicts. &#039;&#039;&#039;As of Obsidian 5.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Operator&#039;&#039; rights allow a user to resubmit failed jobs, submit ad-hoc jobs, interrupt running jobs and pause/resume scheduling hosts. &#039;&#039;&#039;As of Obsidian 5.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Job Folder Rights ===&lt;br /&gt;
&lt;br /&gt;
Understand the [[Admin_Jobs#Nickname.2C_Class_and_Folder|job folder]] feature.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 5.0&#039;&#039;&#039;, more granular rights can be assigned to users that are based on the root job folder specified on a job. Any job that doesn&#039;t have a job folder specified will require the standard rights listed in [[Admin_User_Management#User_Rights|User Rights]] above. For example, if a job has the folder &#039;&#039;Maintenance&#039;&#039; or &#039;&#039;Maintenance/BackOffice&#039;&#039;, a user with the role &#039;&#039;Maintenance-Author&#039;&#039; has the Author permissions as noted below.&lt;br /&gt;
&lt;br /&gt;
For rights that allow creation/modification of jobs, the job folder can only be set/changed by the user if it continues to respect the user&#039;s job folder authoring permissions. This also implies the job folder cannot be cleared by this user. For example, if a user has the roles &#039;&#039;Maintenance-Write&#039;&#039; and &#039;&#039;DevOps-Write&#039;&#039;, they can create a job that has a root folder &#039;&#039;Maintenance&#039;&#039; or &#039;&#039;DevOps&#039;&#039;, optionally with any subpath. The user cannot create a job with no job folder, nor can they change a job&#039;s folder to something other than a root of &#039;&#039;Maintenance&#039;&#039; or &#039;&#039;DevOps&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The 3 permissions supported by folder convention roles are similar, but not identical to the broader roles listed in [[Admin_User_Management#User_Rights|User Rights]] above.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Write&#039;&#039; access by root job folder allows a user to create/modify, submit for ad-hoc and resubmit failed jobs that have a matching root job folder.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Author&#039;&#039; access by root job folder allows a user to create and modify jobs that have a matching root job folder.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;Operator&#039;&#039; access by root job folder allows a user to resubmit, interrupt and submit ad-hocs that have a matching root job folder.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Multi-Factor Authentication (MFA) ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 5.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; MFA does not apply to the [[REST API]], and any user with API privileges will be able to invoke endpoints without an MFA code.&lt;br /&gt;
&lt;br /&gt;
Obsidian supports multi-factor authentication via [https://en.wikipedia.org/wiki/Google_Authenticator Google Authenticator], which generates rotating one-time codes on your mobile device. Other MFA approaches such as email and SMS are not supported, as they have inherent security issues.&lt;br /&gt;
&lt;br /&gt;
To enable MFA, simply configure the [[Admin_Scheduler_Settings|Scheduler Settings]] &#039;&#039;mfaSetupDays&#039;&#039; and &#039;&#039;mfaEnabled&#039;&#039;. Once MFA is enabled, the &#039;&#039;mfaSetupDays&#039;&#039; setting controls how long users have to log in and set up MFA for their account. If they do not log in and complete setup within this time, they will be locked out and an [[#MFA Resets|MFA Reset]] will have to be performed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:MFA scheduler settings.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
=== Completing MFA Setup ===&lt;br /&gt;
&lt;br /&gt;
Once MFA is enabled, when you log in you will see the typical login screen. After entering your user name and password, you will be prompted to perform set up with Google Authenticator. Simply follow the on-screen instructions to scan the presented QR code. Once setup is complete, you will be logged in.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:MFA setup prompt.png]]&lt;br /&gt;
&lt;br /&gt;
=== Logging in After Setup ===&lt;br /&gt;
&lt;br /&gt;
After initial setup is complete, when you log in you will still see the typical login screen. After entering your user name and password, you will be prompted to enter the appropriate code from Google Authenticator to complete login.&lt;br /&gt;
&lt;br /&gt;
=== MFA Resets ===&lt;br /&gt;
&lt;br /&gt;
If a user does not complete MFA setup within the allowed time, or a new user is added when not using Obsidian native authentication, you may need to perform an MFA reset so that they may complete MFA setup and log in. It can also be used to force a user to re-perform MFA setup and invalidate any existing Google Authenticator entries for the account.&lt;br /&gt;
&lt;br /&gt;
When MFA is enabled, the typical user listing table will include a button to perform MFA Resets.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:MFA user listing reset button.png]]&lt;br /&gt;
&lt;br /&gt;
The screen presented after clicking this button will allow you to reset one or more users&#039; MFA state. Either select a known user from the list, or type a user name and click the plus button. Your current user password must also be entered for security reasons.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:MFA reset page.png]]&lt;br /&gt;
&lt;br /&gt;
When you are happy with your changes, click the Reset button.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:MFA reset success.png]]&lt;br /&gt;
&lt;br /&gt;
Users will then be allowed to perform MFA setup for the number of days configured via &#039;&#039;mfaSetupDays&#039;&#039;.&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Admin_Jobs&amp;diff=4134</id>
		<title>Admin Jobs</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Admin_Jobs&amp;diff=4134"/>
		<updated>2026-07-31T14:49:44Z</updated>

		<summary type="html">&lt;p&gt;Craig: Remove pre-4.x version references (4.0.0 is the oldest documented release)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The job screens give you access to the core of Obsidian&#039;s functionality - scheduling and configuring jobs. This screen can be accessed from the primary navigation sidebar, underneath the Configuration parent menu item.. This page discusses listing and modifying jobs. Other job-related features are discussed on other pages in the [[Admin Web Application Guide|guide]].&lt;br /&gt;
&lt;br /&gt;
= Job Listing =&lt;br /&gt;
&lt;br /&gt;
The default screen shows you a table of existing configured jobs.&lt;br /&gt;
&lt;br /&gt;
[[Image:JobListing_4.1.png]]&lt;br /&gt;
== Fields ==&lt;br /&gt;
&lt;br /&gt;
Fields shown in the job table are:&lt;br /&gt;
&lt;br /&gt;
* Job Nickname (links to job configuration screen)&lt;br /&gt;
* Job Class&lt;br /&gt;
* Folder&lt;br /&gt;
* Status (current)&lt;br /&gt;
* Schedule (current)&lt;br /&gt;
* Submit Run Link (users with Write role only)&lt;br /&gt;
* Clone Job Link (users with Write role only)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Submit Run&#039;&#039; either provides the option to submit a job for immediate or scheduled one-time execution.  A job&#039;s current state must be either Enabled, Ad Hoc Active or Unscheduled Active to support immediate one-time execution.  Otherwise, only future scheduled one-time execution is supported.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Clone Job&#039;&#039; opens up the a new job edit screen pre-populated with the selected job&#039;s configuration, including parameters and schedules.&lt;br /&gt;
&lt;br /&gt;
== Filtering ==&lt;br /&gt;
&lt;br /&gt;
Job records can be optionally filtered by host, status and folder. In the case of each, selecting no options is equivalent to choosing all options . Selected options may be cleared by clicking the (X) next to the dropdown arrow.&lt;br /&gt;
&lt;br /&gt;
When filtering by folder, subfolders are matched when searching by a parent folder. Note that intermediate folders with no contents will be suppressed if they do not offer any search value.&lt;br /&gt;
&lt;br /&gt;
== Exporting Results ==&lt;br /&gt;
The current contents of the table can be exported to Excel, CSV or XML by clicking on export icon displayed at the far right of the table header. The download will automatically begin and will include all pages of the current table of results.&lt;br /&gt;
&lt;br /&gt;
== Ad Hoc &amp;amp; One-Time Run Submission ==&lt;br /&gt;
&lt;br /&gt;
You may submit a job for a single execution by clicking the &#039;&#039;Submit Run&#039;&#039; link in the job listing, which will bring you to a screen to configure your submission.&lt;br /&gt;
&lt;br /&gt;
One-time runs can be submitted either for immediate or scheduled one-time execution. A job&#039;s current state must be either Enabled, Ad Hoc Active or Unscheduled Active to support ad hoc or one-time runs. &lt;br /&gt;
&lt;br /&gt;
The Scheduled Time can be omitted when immediate execution is desired, assuming it is supported as just described.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:AdHocJobSubmission_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
=== Run Parameters ===&lt;br /&gt;
Obsidian allows you to specify [[Implementing_Jobs#Ad_Hoc_.26_One-Time_Run_Parameters|run parameters]] which are supplied to the job for a single execution. To add a parameter, click the &#039;&#039;Add Parameter&#039;&#039; button. You may add values by clicking the (+) and trash buttons to the right of the field. Any entered values will be validated against the selected type, plus against any parameter of the same name defined at the job level, to ensure data types and other restrictions are enforced.  If a parameter name for a run parameter has the same name as a configured job parameter, the job parameter values are dropped, and the run parameter values are used instead.&lt;br /&gt;
&lt;br /&gt;
To remove a parameter and its values, click the &#039;&#039;Remove&#039;&#039; button.&lt;br /&gt;
&lt;br /&gt;
If you reference a [[Admin_Global_Parameters|global parameter]] by entering a value surrounded by double curly braces (e.g. &amp;lt;code&amp;gt;&amp;amp;#123;&amp;amp;#123;param&amp;amp;#125;&amp;amp;#125;&amp;lt;/code&amp;gt;), Obsidian will resolve this to the values that are configured for the corresponding global parameter (e.g. &amp;lt;code&amp;gt;param&amp;lt;/code&amp;gt;). When the job is executed, Obsidian will use the global parameter&#039;s values in place of the global parameter reference. You may use multiple global parameters for a given run parameter by specifying each in its own value field, and they can be combined with regular parameter values, also in their own fields. All imported global parameter values will be validated against applicable job parameter definitions and data types.&lt;br /&gt;
&lt;br /&gt;
= Adding &amp;amp; Editing Jobs = &lt;br /&gt;
&lt;br /&gt;
To add a new job, click the (+) button in the job listing table header.&lt;br /&gt;
&lt;br /&gt;
To edit or view a job&#039;s configuration, click the nickname in the results table.&lt;br /&gt;
&lt;br /&gt;
The job edit screen contains multiple configuration items with which you should be familiar. They are outlined in the sections below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The question mark icon next to form titles indicates inline help. Click on the icon to view help related to the current item. This is your best source of help when making changes to job details.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:AddJob_4.1.png|300px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Job Settings ==&lt;br /&gt;
&lt;br /&gt;
=== Nickname, Class and Folder === &lt;br /&gt;
All jobs must have a unique job nickname which identifies the job in logs and event notifications.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;Job Class&#039;&#039; selector allows you to choose existing jobs that Obsidian knows about, and you may also manually type in a custom job (hit enter to confirm and validate). When selecting a job, note that it must be available to all the scheduler nodes (i.e. on the classpath) so that it can retrieve parameter information and validate your configuration. It is not required, nor is it suggested, to keep these classes in the web admin application.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; When selecting or choosing a new job, the defined and custom parameters sections will refresh based on that specific job&#039;s parameter definitions.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;Folder&#039;&#039; selector allows you to select an in use folder, or type in a new folder. Clicking the (x) button will clear the selection so you can type in a custom folder. Jobs can be assigned to folders to help grouping and searching for them. You can use nested paths (e.g. A/B) using forward-slashes. Note that back-slashes will be automatically converted to forward slashes, and multiple slashes in succession are collapsed into a single slash. Leading and trailing slashes are stripped for consistency.&lt;br /&gt;
&lt;br /&gt;
=== Advanced Options ===&lt;br /&gt;
&lt;br /&gt;
This section allows you to specify options related to how jobs are picked up and recovered in exceptional circumstances. Click on &#039;&#039;Advanced Options&#039;&#039; or the chevron next to it to expand or collapse this section.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Recovery Type&#039;&#039; allows you to specify how the job will be handled when execution does not happen normally within the &#039;&#039;Pickup Buffer&#039;&#039; because of conflicts, server downtime, etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Startup/Shutdown Mode&#039;&#039; allows you to specify if the job should fire automatically, independently of any defined schedule, in various startup and shutdown scenarios.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Pickup Buffer&#039;&#039; indicates for how many minutes the job is considered within normal execution time from the exact scheduled time. This allows for leniency when dealing with conflicting jobs or with short-term downtime. If a minor delay isn&#039;t significant to your job, using a higher buffer can help prevent jobs from being marked missed or conflict missed unnecessarily.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Auto Retries - #&#039;&#039; allows you to indicate the number of times you wish Obsidian to automatically resubmit (retry) the job for execution when triggered by a non-interrupted execution failure.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Auto Retries - Interval&#039;&#039; allows you to indicate the minimum interval in minutes from the point in time the job failed and the job will be retried.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Auto Retries - Exponential&#039;&#039; allows you to indicate if you desire the auto retries interval to exponentially increase as retries are attempted. For example, if you set the interval minutes to 5 and check &#039;&#039;Exponential&#039;&#039;, the first retry will be 5 minutes after failure, the second retry after a subsequent failure will be 10 minutes later, then 20 minutes, and so on.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Expected Length&#039;&#039; allows you to trigger event notifications in the event that the job execution is shorter or longer than the durations specified. Both fields are optional, and you are permitted to specify only one of the two. Specify a duration followed by a unit indicator (s-seconds, m-minutes, h-hours), e.g. 45s, 10m or 1h. To receive notifications based on the job running outside these parameters, configure [[Admin Subscribers|subscribers]] at the Warning level or above.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Auto Interrupt&#039;&#039; allows you to specify if you wish an [[Implementing_Jobs#Interruptable_Jobs|Interruptable Job]] to be interrupted when its maximum expected run length is exceeded. Only available as an option for those jobs that adhere to the [[Implementing_Jobs#Interruptable_Jobs|Interruptable Job]] requirements and have a maximum expected length specified.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Fixed Hosts&#039;&#039; allows you to indicate that only certain hosts should run this job. You can select one or more hosts which Obsidian knows about. If a host is not listed and will be started later, simply type in the name and click the (+) button to add it to the list. A configuration item allows you to specify whether you wish host restrictions to apply on AdHoc jobs or not. It defaults to TRUE on new installations. This can be found under &#039;&#039;&#039;[[Admin_Scheduler_Settings|Scheduler Settings]]/Job/adHocJobsRespectFixedHostsRestrictions&#039;&#039;&#039;. As of version 4.4, you may use a Java regular expression to use pattern matching on the host name. For example, a wildcard match for hosts that start with &amp;quot;abc&amp;quot; can be done with a pattern like &amp;quot;abc.*&amp;quot;. To implement a blacklist of hosts that the job should not run on, a single pattern with negation can be used: &amp;quot;(?!(blacklist-one|blacklist-two)).*&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Host Preference&#039;&#039; indicates that Fixed Hosts are only preferred hosts, and that other available hosts can run the job when none of the preferred hosts are running. When selected, you can also order the hosts using drag &amp;amp; drop. Obsidian will always choose the highest priority host to run the job if one is available.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Chain All&#039;&#039; allows you  allows you specify the behaviour when this job is to be chained but is executing another instance at that moment. By default, only one newly chained instance will be queued. Choose this option to chain all such occurrences. Each chained target instance is queued for the same minute. Ensure your Pickup Buffer is set large enough for these to be picked up and run in sequence.&lt;br /&gt;
&lt;br /&gt;
== Schedule ==&lt;br /&gt;
&lt;br /&gt;
When adding a new job, the schedule section will allow you to specify the initial schedule. You will have the ability to overwrite, adjust or add new schedules after initial creation. Schedule is required only for enabled jobs and is ignored for other states. See [[#Date Fields|Date Fields]] for details on how the date and time fields can be used. [[Cron]] patterns are used to specify the schedule or you may use custom [[Cron#Recurrence | Recurrence]] expressions. &lt;br /&gt;
&lt;br /&gt;
Multiple cron patterns may be specified for a single schedule by delimiting them with a semi-colon (e.g. &amp;lt;code&amp;gt;35 8 * * * *;20 12 * * *;40 16 * * *&amp;lt;/code&amp;gt;). This means that the job will fire if it matches any of the supplied patterns.&lt;br /&gt;
&lt;br /&gt;
If you specify start and end dates, any dates outside this range will be initially set to the Disabled States.&lt;br /&gt;
&lt;br /&gt;
=== State ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Enabled&#039;&#039; is the standard state that allows scheduling, chaining, and one-time run submissions. This is the only state that takes a schedule [[Cron]] pattern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Disabled&#039;&#039; allows you to disable the job completely during the specified window.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Chain Active&#039;&#039; allows the job to be executed as a chain target only.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Ad Hoc Active&#039;&#039; allows the job to be submitted and executed for one-time runs only. Note that a one-time run must be requested after saving your job configuration for the job to be executed.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Unscheduled Active&#039;&#039; allows the job to be executed as a chain target and also allows the job to be submitted and executed for one-time runs upon request. Note that a one-time run must be requested after saving your job configuration for the job to be executed.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; When editing a job, the schedule section shows a read-only view of the active schedule, or if one is not active yet, the first schedule that will become active.&lt;br /&gt;
&lt;br /&gt;
=== Date Fields ===&lt;br /&gt;
&lt;br /&gt;
Dates and times are used to specify schedule start and end times. &lt;br /&gt;
&lt;br /&gt;
From and To fields will show user-friendly date and time selectors which enforce a valid date range. If you cannot select a date in the past or future, it is because the date widget is enforcing a valid time, and you may need to edit your from or to times appropriately.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; If you wish minute-level specificity, simply manually type your time into the time field in the 24-hour format HH:MM. You do not have to use the time selector.&lt;br /&gt;
&lt;br /&gt;
There is some helpful behaviour when you wish a schedule to be indefinite and immediately active. You do not have to specify times in this common case:&lt;br /&gt;
&lt;br /&gt;
* Blank from dates will be treated as today&#039;s date.&lt;br /&gt;
* Blank from times will be treated as the next valid minute, unless a non-current date is specified, in which case it is treated as midnight.&lt;br /&gt;
* Blank to dates will be considered as indefinite (no end).&lt;br /&gt;
* Blank to times will be considered as end of day (11:59 PM), or indefinite when the to date is blank.&lt;br /&gt;
&lt;br /&gt;
=== Edit, View &amp;amp; Change Schedules ===&lt;br /&gt;
&lt;br /&gt;
After you have saved the initial job configuration, you can then make schedule changes and additions. &lt;br /&gt;
&lt;br /&gt;
When saving a new job or loading an existing job&#039;s configuration, you will see the current schedule in a read-only view in the &#039;&#039;Schedule&#039;&#039; section.&lt;br /&gt;
&lt;br /&gt;
To update the current schedule or create a future schedule, click the &#039;&#039;Edit&#039;&#039; button, then make your changes. New schedules may overlap, contain or be within existing schedules with no issue. Obsidian will automatically split and merge schedules to create a complete schedule based on your changes. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:JobSchedule_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
If you wish to abandon your schedule changes, click the &#039;&#039;Revert&#039;&#039; button.&lt;br /&gt;
&lt;br /&gt;
[[Image:JobScheduleRevert_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
To simply replace any current schedules with a new permanent schedule, leave the time fields blank, and it will become active indefinitely at the beginning of the next minute.&lt;br /&gt;
&lt;br /&gt;
Clicking the &#039;&#039;View All&#039;&#039; button will show all schedules that have been configured that are active now or in the future. The &#039;&#039;Include Expired&#039;&#039; checkbox can be used to include expired schedules in the listing. This will be the existing schedules if no new schedule is entered, or a preview of the new altered schedules based on your input if specified. This allows you to view the effect of your changes before confirming them.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; In order to save your schedule changes, you&#039;ll need to save the job itself. If you click the &#039;&#039;Revert&#039;&#039; button after making schedule changes, they will be discarded and will not be included if you then click Save.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Below you can see the &#039;&#039;View All&#039;&#039; button being used to view the current schedules without making any changes.&lt;br /&gt;
&lt;br /&gt;
[[Image:NewJobSchedule_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
=== Custom Calendar Assignment ===&lt;br /&gt;
&lt;br /&gt;
An Enabled Schedule may also be assigned a [[Admin_Custom_Calendars|Custom Calendar]]. Use this to prevent scheduling/execution on the dates specified by the Custom Calendar.&lt;br /&gt;
&lt;br /&gt;
[[Image:CustomCalendarAssignment_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
&lt;br /&gt;
=== Defined Parameters ===&lt;br /&gt;
&lt;br /&gt;
If a job has defined parameters as discussed in [[Implementing Jobs]], this section will show the configurable parameters. The label on the left will indicate the name and type of the parameter.&lt;br /&gt;
&lt;br /&gt;
If the parameter definition allows multiple values, buttons to the right of the text field will allow you to add or remove values. Note that remove will not appear for the first item, and if you wish to omit the parameter value, simply leave the first field blank. &lt;br /&gt;
&lt;br /&gt;
If the parameter is defined to use a [[Implementing_Jobs#List_Parameterization|list of values]], a select list will be presented instead of a text box.&lt;br /&gt;
&lt;br /&gt;
Asterisks indicate required values.&lt;br /&gt;
&lt;br /&gt;
=== Custom Parameters ===&lt;br /&gt;
&lt;br /&gt;
In addition to defined parameters, you always have the option of specifying custom parameters, which the job may use in a dynamic fashion. In the &#039;&#039;Parameters&#039;&#039; section, click on &#039;&#039;Custom&#039;&#039; or the chevron next to it to expand or collapse this section.&lt;br /&gt;
&lt;br /&gt;
To add a custom parameter, click the &#039;&#039;Add Custom Parameter&#039;&#039; button. Custom parameters always allow multiple values since they have no fixed definition, and you may add values by clicking the (+) and trash buttons to the right of the field. Any entered values will be validated against the selected type, and the selected type will determine how they are stored and provided to the job.&lt;br /&gt;
&lt;br /&gt;
To remove a custom parameter and its values, click the &#039;&#039;Remove&#039;&#039; button.&lt;br /&gt;
&lt;br /&gt;
Custom parameters support global parameter references. As of Obsidian 7.0.0, autocomplete and hover tooltips for global parameter references are also available on custom parameter fields (see [[#Global_Parameters|Global Parameters]] above).&lt;br /&gt;
&lt;br /&gt;
=== Global Parameters ===&lt;br /&gt;
&lt;br /&gt;
Both defined and custom parameters support the usage of [[Admin_Global_Parameters|global parameters]]. These let you configure job parameters globally, and then simply import them into jobs as needed.&lt;br /&gt;
&lt;br /&gt;
To use a global parameter in a job, simply enter its name surrounded by double curly braces (e.g. &amp;lt;code&amp;gt;&amp;amp;#123;&amp;amp;#123;param&amp;amp;#125;&amp;amp;#125;&amp;lt;/code&amp;gt;) in the value field, and Obsidian will locate the matching global parameter (e.g. &amp;lt;code&amp;gt;param&amp;lt;/code&amp;gt;). When the job is executed, Obsidian discards the global parameter reference and replaces it with the current global parameter values. You may use multiple global parameters for a given parameter by specifying each in its own value field, and they can be combined with regular parameter values, also in their own fields. All imported global parameter values will be validated against applicable job parameter definitions and data types. Note that Obsidian does not support global parameter references embedded inside parameter values, since it does not perform text substitution - only parameter values containing only the global parameter reference will be replaced with the global parameter value.&lt;br /&gt;
&lt;br /&gt;
When you save your job changes, Obsidian will validate that any reference global parameter values match your defined data types and other parameter restrictions.&lt;br /&gt;
&lt;br /&gt;
See [[Implementing_Jobs#Global_Parameters|Parameterization - Global Parameters]] for more details on type handling and validation of global parameters.&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 7.0.0, when editing a job, typing &#039;&#039;{{&#039;&#039; in a parameter value field opens an autocomplete suggestion list of available global parameter names. Select a name with Enter or a click, or dismiss with Esc. The suggestions are subject to your user&#039;s permissions and configuration settings — see [[Advanced_Configuration|Advanced Configuration]] for details.&lt;br /&gt;
&lt;br /&gt;
On the job &#039;&#039;&#039;view&#039;&#039;&#039; screen (read-only), hovering over a parameter value that contains &#039;&#039;{{Name}}&#039;&#039; shows a tooltip with the resolved global parameter value, when your user has the necessary permissions.&lt;br /&gt;
&lt;br /&gt;
== Saving ==&lt;br /&gt;
&lt;br /&gt;
To save your configuration changes, click the Save button at the bottom of the form or in the left navigation area. If there are problems with your changes, you will be notified at the top of the screen. You can then correct your entered information and re-save. Upon success, you will see a success confirmation.&lt;br /&gt;
&lt;br /&gt;
== Deleting ==&lt;br /&gt;
&lt;br /&gt;
You can delete a job and all its history and configuration from Obsidian.  We recommend you use this feature sparingly. It is most useful on long-obsolete jobs.  When you attempt to delete a job, Obsidian will ask for your confirmation along with prompting you with a choice.  &lt;br /&gt;
&lt;br /&gt;
Deleting a job in Obsidian assumes you are fine with losing all runtime results, defined parameters and any schedules and states.  You will also lose any chain references from this job, i.e if a job was chained from the job you&#039;re deleting, it will show as chained, but the source job information will be lost.  Since jobs can have an impact on other jobs, namely chaining and conflict configuration, the default delete will fail to complete if there are any active chains referencing the job in question or if the job is included in any conflict groups.  Optionally, you can also select the option &#039;&#039;Delete all active job configuration?&#039;&#039; in the Delete Job window and this will delete any of these as well.&lt;br /&gt;
&lt;br /&gt;
[[Image:DeleteJobWindow_4.0.png]]&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Advanced_Configuration&amp;diff=4133</id>
		<title>Advanced Configuration</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Advanced_Configuration&amp;diff=4133"/>
		<updated>2026-07-31T02:18:21Z</updated>

		<summary type="html">&lt;p&gt;Craig: /* Dependent Libraries */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian requires some initial configuration parameters for database connection information and desired authentication mechanisms.  The installer takes care of configuring these as provided.  For your reference should you wish to manually create/edit these, note the details below.&lt;br /&gt;
&lt;br /&gt;
== Configuration Details ==&lt;br /&gt;
&lt;br /&gt;
The following sections show the available properties and sample values that can be used in the &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; properties file. While properties format is shown, all the same configuration items apply to yaml support. See [[#Properties.2FYaml_File|Properties/Yaml File]] for more details on this file.&lt;br /&gt;
&lt;br /&gt;
=== Authentication Properties ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Comment out the native authenticator setup&lt;br /&gt;
#com.carfey.suite.security.Authenticator=com.carfey.suite.security.DBAuthenticator&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LDAP Authentication Properties ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#Set your LDAP info here&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.LdapAuthenticator&lt;br /&gt;
# As of Obsidian 2.1.1, you can specify the attribute type used in building up the distinguished name (dn). If unspecified, defaults to cn&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.dn.attribute=uid&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of dn.bases to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.dn.base=ou=people,o=MyOrgHere&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.url=ldap://localhost:10389&lt;br /&gt;
# As of Obsidian 2.1.1, you can specify the SECURITY_AUTHENTICATION. Defaults to simple. &lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.securityAuthentication=simple&lt;br /&gt;
# Any necessary additional information such as Provider, Principal and Host will need to be handled in your custom class extending LdapAuthenticator.&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 3.5.1, you can login via an LDAP attribute that is not part of the dn. A matching entry is found in the LDAP Directory (using anonymous or a fixed lookup account) &lt;br /&gt;
# after which the dn attribute above is retrieved to perform the user&#039;s authentication. The following 5 attributes are only used for this type of lookup and authentication.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.lookupDnAttribute=false&lt;br /&gt;
# As of Obsidian 3.5.1, this is the attribute name that is being searched for in the LDAP directory to build the dn for eventual authentication.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.loginAttribute=sAMAccountName&lt;br /&gt;
# As of Obsidian 3.5.1, this determines if these lookups will be done anonymously (without a session authenticated by user/password)&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.anonymousEnabled=false&lt;br /&gt;
# As of Obsidian 3.5.1, if anonymous lookups are not permitted or desired, provide the dn and password to be used for lookups. Typically, a read-only account with &lt;br /&gt;
# read rights to the LDAP entries and attributes in question is sufficient.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchUserFullDn=cn=ObsidianLDAPReadOnly,ou=people,o=MyOrgHere&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchPassword=password&lt;br /&gt;
# As of Obsidian 4.5.1, an alternate search base configuration parameter is used for the searchUser&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchUserDnBase=ou=people,o=MyOrgHere&lt;br /&gt;
# As of Obsidian 4.7.1, you may specify one or more additional group membership attributes beyond the defaults&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.groupMembershipAttribute=businessGroup~~~departmentName&lt;br /&gt;
&lt;br /&gt;
# Configure who may access the web app, based on LDAP group membership&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.accessDN=cn=SchedulerAccess,ou=groups,o=MyOrgHere&lt;br /&gt;
&lt;br /&gt;
# Configure the Write role used in the admin web app, based on LDAP group membership (by default, users may only read)&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.write.dn=cn=SchedulerWrite,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.write.roleName=Write&lt;br /&gt;
&lt;br /&gt;
# Configure the Admin role used in the admin web app, based on LDAP group membership (users may configure system parameters, etc).&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.admin.dn=cn=SchedulerAdmin,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.admin.roleName=Admin&lt;br /&gt;
&lt;br /&gt;
# Configure the LimitedRead role used in the admin web app, based on LDAP group membership (by default, users may only read)&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.limitedRead.dn=cn=SchedulerLimitedRead,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.limitedRead.roleName=LimitedRead&lt;br /&gt;
&lt;br /&gt;
# Configure the APIrole used by the REST API, based on LDAP group membership&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.api.dn=cn=SchedulerAPI,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.api.roleName=API&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OAuth / OIDC Authentication Properties ====&lt;br /&gt;
&lt;br /&gt;
To use OAuth / OIDC SSO, set the &#039;&#039;Authenticator&#039;&#039; property to&lt;br /&gt;
&#039;&#039;com.carfey.suite.security.OAuthAuthenticator&#039;&#039; and add the properties below. All&lt;br /&gt;
properties are prefixed with &#039;&#039;com.carfey.suite.security.OAuthAuthenticator.&#039;&#039; (shown&lt;br /&gt;
without prefix in the table for brevity).&lt;br /&gt;
&lt;br /&gt;
===== Required properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;issuer&#039;&#039; || OIDC Issuer URL. Obsidian fetches &#039;&#039;&amp;lt;issuer&amp;gt;/.well-known/openid-configuration&#039;&#039; lazily on first use and caches it.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clientId&#039;&#039; || Client ID registered at the IdP.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clientSecret&#039;&#039; || Client secret. Keep out of version control.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;redirectUri&#039;&#039; || Exact callback URL registered at the IdP (e.g. &#039;&#039;https://app.example.com/oauth/callback&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;scopes&#039;&#039; || Space-separated OAuth scopes (e.g. &#039;&#039;openid profile email groups&#039;&#039;). Must include &#039;&#039;openid&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groups&#039;&#039; || Name of the token claim that carries group memberships (e.g. &#039;&#039;groups&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.group&#039;&#039; || IdP group value for this mapping. &#039;&#039;&amp;lt;idx&amp;gt;&#039;&#039; is an arbitrary stable label (numeric or descriptive).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.roleName&#039;&#039; || Obsidian role for this mapping. At least one &#039;&#039;Admin&#039;&#039; mapping is required. Valid roles: &#039;&#039;&#039;Admin&#039;&#039;&#039;, &#039;&#039;&#039;API&#039;&#039;&#039;, &#039;&#039;&#039;Write&#039;&#039;&#039;, &#039;&#039;&#039;LimitedRead&#039;&#039;&#039;, &#039;&#039;&#039;Operator&#039;&#039;&#039;, &#039;&#039;&#039;Author&#039;&#039;&#039;, and folder-based roles (&#039;&#039;&amp;lt;folder&amp;gt;-(Write|Author|Operator)&#039;&#039;).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Optional properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Default !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;displayName&#039;&#039; || (none) || Label on the SSO button: &amp;quot;Sign in with &#039;&#039;&amp;lt;displayName&amp;gt;&#039;&#039;&amp;quot;. Defaults to &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;logout.mode&#039;&#039; || &#039;&#039;local&#039;&#039; || &#039;&#039;local&#039;&#039;: invalidates Obsidian session only. &#039;&#039;rp&#039;&#039;: RP-initiated logout via IdP &#039;&#039;end_session_endpoint&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;postLogoutRedirectUri&#039;&#039; || (none) || Where to redirect after RP-initiated logout. Ignored when &#039;&#039;logout.mode=local&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.user&#039;&#039; || &#039;&#039;sub&#039;&#039; || Claim used as the internal user ID in audit logs. &#039;&#039;&#039;Strongly recommend keeping as &#039;&#039;sub&#039;&#039;&#039;&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.displayName&#039;&#039; || &#039;&#039;preferred_username&#039;&#039; || Claim shown in the Obsidian UI nav bar. Separate from &#039;&#039;claim.user&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.validateTyp&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, rejects Bearer tokens where &#039;&#039;typ&#039;&#039; is present but not &#039;&#039;at+JWT&#039;&#039;. Set &#039;&#039;false&#039;&#039; only for legacy IdPs that omit &#039;&#039;typ&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clockSkewSeconds&#039;&#039; || &#039;&#039;60&#039;&#039; || Clock skew tolerance for &#039;&#039;exp&#039;&#039;/&#039;&#039;nbf&#039;&#039; validation (seconds).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;metadata.ttlHours&#039;&#039; || &#039;&#039;24&#039;&#039; || OIDC discovery document cache TTL in hours.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;expectedAccessTokenAudience&#039;&#039; || &#039;&#039;clientId&#039;&#039; || Expected &#039;&#039;aud&#039;&#039; for Bearer access tokens. See security note below.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsSource&#039;&#039; || &#039;&#039;idtoken&#039;&#039; || Where to read groups during browser SSO: &#039;&#039;idtoken&#039;&#039;, &#039;&#039;accesstoken&#039;&#039;, or &#039;&#039;userinfo&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsBearerSource&#039;&#039; || &#039;&#039;accesstoken&#039;&#039; || Where to read groups on the Bearer path: &#039;&#039;idtoken&#039;&#039; or &#039;&#039;accesstoken&#039;&#039;. &#039;&#039;&#039;userinfo is not permitted on the Bearer path.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsValuePath&#039;&#039; || (none) || Sub-field name when groups is an array of objects (e.g. &#039;&#039;name&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsStripPrefix&#039;&#039; || (none) || Prefix to strip from each group value (e.g. &#039;&#039;/&#039;&#039; for Keycloak full-path style).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsUserinfoTimeout&#039;&#039; || &#039;&#039;5&#039;&#039; || HTTP timeout in seconds for the userinfo endpoint. Only used when &#039;&#039;claim.groupsSource=userinfo&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.prefix&#039;&#039; || (none) || Prefix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.suffix&#039;&#039;. Map is rebuilt at startup; adding a new root folder requires a restart.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.suffix&#039;&#039; || (none) || Suffix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.prefix&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;requireRole&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, browser SSO fails if IdP groups map to no Obsidian roles. Does not apply to REST Bearer tokens.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Security notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;expectedAccessTokenAudience and cross-JWT confusion&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When &#039;&#039;expectedAccessTokenAudience&#039;&#039; is not set it defaults to &#039;&#039;clientId&#039;&#039;. In many OIDC&lt;br /&gt;
configurations the id_token also carries &#039;&#039;aud=clientId&#039;&#039;, which could allow an id_token&lt;br /&gt;
to be submitted as a Bearer token. The &#039;&#039;claim.validateTyp=true&#039;&#039; default mitigates this&lt;br /&gt;
for compliant IdPs (access tokens carry &#039;&#039;typ=at+JWT&#039;&#039;; id_tokens carry &#039;&#039;typ=JWT&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
For maximum defence, set &#039;&#039;expectedAccessTokenAudience&#039;&#039; to a distinct resource-server&lt;br /&gt;
identifier if your IdP supports it.&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;claim.validateTyp=false&#039;&#039; AND &#039;&#039;expectedAccessTokenAudience&#039;&#039; is not explicitly set,&lt;br /&gt;
Obsidian logs a mandatory startup WARN advising you to set &#039;&#039;expectedAccessTokenAudience&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
===== Keycloak example =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.OAuthAuthenticator&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.issuer=http://localhost:8081/realms/obsidian&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.clientId=obsidian-client&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.clientSecret=test-client-secret-local&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.redirectUri=http://localhost:8080/oauth/callback&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.scopes=openid profile email groups&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.claim.groups=groups&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.claim.groupsSource=idtoken&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.1.group=Obsidian-Admins&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.1.roleName=Admin&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.2.group=Obsidian-Write&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.2.roleName=Write&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.3.group=Obsidian-Read&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.3.roleName=LimitedRead&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.4.group=Obsidian-API&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.4.roleName=API&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.displayName=Keycloak&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.logout.mode=local&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.requireRole=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Provider notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Microsoft Entra ID&#039;&#039;&#039;: groups claim contains GUIDs by default; configure Optional&lt;br /&gt;
Claims for display names. Accounts with 200+ groups trigger overage. Multi-tenant apps not&lt;br /&gt;
supported in v1.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auth0&#039;&#039;&#039;: opaque access tokens (Auth0 default) are not supported on the Bearer path.&lt;br /&gt;
Configure a custom API audience to get JWT access tokens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Okta&#039;&#039;&#039;: use the Custom Authorization Server URL, not the Org Authorization Server.&lt;br /&gt;
&lt;br /&gt;
===== v1 limitations =====&lt;br /&gt;
&lt;br /&gt;
* Opaque access tokens not supported on the REST/Bearer path.&lt;br /&gt;
* No &#039;&#039;jti&#039;&#039; replay protection.&lt;br /&gt;
* Microsoft Entra multi-tenant apps not supported.&lt;br /&gt;
* No post-login IdP session validity check.&lt;br /&gt;
* Dynamic role map requires restart for new root folders.&lt;br /&gt;
* Installer UI supports up to 10 static role pairs; runtime supports unlimited.&lt;br /&gt;
&lt;br /&gt;
==== SAML Authentication Properties ====&lt;br /&gt;
&lt;br /&gt;
To use SAML 2.0 SSO, set the &#039;&#039;Authenticator&#039;&#039; property to&lt;br /&gt;
&#039;&#039;com.carfey.suite.security.saml.SAMLAuthenticator&#039;&#039; and add the properties below. All&lt;br /&gt;
properties are prefixed with &#039;&#039;com.carfey.suite.security.saml.SAMLAuthenticator.&#039;&#039; (shown&lt;br /&gt;
without prefix in the table for brevity). Note the &#039;&#039;.saml.&#039;&#039; package segment in both values.&lt;br /&gt;
&lt;br /&gt;
Obsidian acts as a SAML Service Provider and supports SP-initiated browser login only. See&lt;br /&gt;
[[Authenticator#SAMLAuthenticator_(SAML_2.0_SSO)|Authenticator — SAMLAuthenticator]] for how the&lt;br /&gt;
login and logout flows behave and for the list of &#039;&#039;/saml&#039;&#039; endpoints.&lt;br /&gt;
&lt;br /&gt;
===== Required properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;entityId&#039;&#039; || SP entity ID registered at the IdP (e.g. &#039;&#039;https://app.example.com/obsidian&#039;&#039;). Obsidian also requires this value to appear in the assertion&#039;s &#039;&#039;AudienceRestriction&#039;&#039; when the IdP sends one.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;assertionConsumerServiceUrl&#039;&#039; || Exact ACS URL where the IdP posts responses. This must be Obsidian&#039;s callback endpoint, e.g. &#039;&#039;https://app.example.com/obsidian/saml/callback&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;idpMetadataUrl&#039;&#039; &#039;&#039;&#039;or&#039;&#039;&#039; &#039;&#039;idpMetadataFile&#039;&#039; || Source of the IdP federation metadata: an HTTP(S) URL, or an absolute path to a local XML file. Set exactly one of the two.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups&#039;&#039; || Name of the assertion attribute carrying group memberships (e.g. &#039;&#039;groups&#039;&#039;). Required unless &#039;&#039;dynamicRole.prefix&#039;&#039; is set instead.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.group&#039;&#039; || IdP group value for this mapping. &#039;&#039;&amp;lt;idx&amp;gt;&#039;&#039; is an arbitrary stable label (numeric or descriptive). Not needed if all roles come from &#039;&#039;dynamicRole&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.roleName&#039;&#039; || Obsidian role for this mapping. The installer requires at least one &#039;&#039;Admin&#039;&#039; mapping, and you need one in practice so that somebody can administer Obsidian. Valid roles: &#039;&#039;&#039;Admin&#039;&#039;&#039;, &#039;&#039;&#039;Write&#039;&#039;&#039;, &#039;&#039;&#039;LimitedRead&#039;&#039;&#039;, &#039;&#039;&#039;Operator&#039;&#039;&#039;, &#039;&#039;&#039;Author&#039;&#039;&#039;, and folder-based roles (&#039;&#039;&amp;lt;folder&amp;gt;-(Write|Author|Operator)&#039;&#039;). The &#039;&#039;API&#039;&#039; role has no effect on a SAML install, because the REST API is unavailable.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Because &#039;&#039;signRequests&#039;&#039; defaults to enabled, a default configuration also requires the three&lt;br /&gt;
&#039;&#039;keystore&#039;&#039; properties below. Disable &#039;&#039;signRequests&#039;&#039; only if your IdP does not require signed&lt;br /&gt;
authentication requests.&lt;br /&gt;
&lt;br /&gt;
===== Optional properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Default !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;displayName&#039;&#039; || (none) || Label on the SSO button: &amp;quot;Sign in with &#039;&#039;&amp;lt;displayName&amp;gt;&#039;&#039;&amp;quot;. Defaults to &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;signRequests&#039;&#039; || &#039;&#039;true&#039;&#039; || Signs the outbound &#039;&#039;AuthnRequest&#039;&#039; and SP &#039;&#039;LogoutRequest&#039;&#039;. Requires the three &#039;&#039;keystore&#039;&#039; properties.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.path&#039;&#039; || (none) || Path to the keystore holding the SP signing key. Required when &#039;&#039;signRequests&#039;&#039; is enabled.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.password&#039;&#039; || (none) || Keystore password. Keep out of version control.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.alias&#039;&#039; || (none) || Alias of the SP signing key within the keystore.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;wantAssertionsSigned&#039;&#039; || &#039;&#039;true&#039;&#039; || Requires the IdP to sign assertions, verified against the signing certificate in the IdP metadata.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;singleLogoutServiceUrl&#039;&#039; || (none) || SP single logout URL. When set it is published in Obsidian&#039;s SP metadata and is matched against the &#039;&#039;Destination&#039;&#039; of an incoming &#039;&#039;LogoutRequest&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;logout.mode&#039;&#039; || &#039;&#039;local&#039;&#039; || &#039;&#039;local&#039;&#039;: invalidates the Obsidian session only. &#039;&#039;slo&#039;&#039;: SP-initiated single logout via the IdP&#039;s HTTP-Redirect &#039;&#039;SingleLogoutService&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;postLogoutRedirectUri&#039;&#039; || (none) || Where to redirect after the IdP&#039;s &#039;&#039;LogoutResponse&#039;&#039;. When blank the browser returns to &#039;&#039;/&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;nameIdFormat&#039;&#039; || &#039;&#039;urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified&#039;&#039; || &#039;&#039;NameIDPolicy&#039;&#039; format requested from the IdP. A transient format logs a startup warning, because transient NameIDs cannot serve as stable user IDs.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.user&#039;&#039; || &#039;&#039;nameId&#039;&#039; || Assertion attribute used as the internal user ID in audit logs. &#039;&#039;&#039;Strongly recommend keeping as &#039;&#039;nameId&#039;&#039;&#039;&#039;&#039;; any other value logs a startup warning.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.displayName&#039;&#039; || (none) || Attribute shown in the Obsidian UI nav bar. Separate from &#039;&#039;attribute.user&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.friendlyName&#039;&#039; || (none) || Match the groups attribute on its &#039;&#039;FriendlyName&#039;&#039; instead of its &#039;&#039;Name&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.valuePath&#039;&#039; || (none) || Sub-field name when the groups attribute holds structured values.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.stripPrefix&#039;&#039; || (none) || Prefix to strip from each group value (e.g. &#039;&#039;/&#039;&#039; for Keycloak full-path style).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;metadata.ttlHours&#039;&#039; || &#039;&#039;24&#039;&#039; || IdP federation metadata cache TTL in hours. A failed refresh falls back to the cached copy with a warning.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clockSkewSeconds&#039;&#039; || &#039;&#039;60&#039;&#039; || Clock skew tolerance for &#039;&#039;NotBefore&#039;&#039; / &#039;&#039;NotOnOrAfter&#039;&#039; validation (seconds).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;forceAuthn&#039;&#039; || &#039;&#039;false&#039;&#039; || Sets &#039;&#039;ForceAuthn=&amp;quot;true&amp;quot;&#039;&#039; on the &#039;&#039;AuthnRequest&#039;&#039;, asking the IdP to reauthenticate the user.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;authnContextClassRef&#039;&#039; || (none) || Requests a specific &#039;&#039;RequestedAuthnContext&#039;&#039;. A differing context in the response is logged as a warning, not rejected.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.prefix&#039;&#039; || (none) || Prefix for auto-generated folder-role group mappings. Usable on its own. The map is built at startup, so adding a new root job folder requires a restart.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.suffix&#039;&#039; || (none) || Optional suffix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.prefix&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;requireRole&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, browser SSO fails if the user&#039;s group attributes map to no Obsidian roles.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Security notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Encrypted assertions are not supported&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Obsidian requires signed, unencrypted assertions. If your SP metadata carries no signing key&lt;br /&gt;
material, Obsidian logs a startup warning: IdPs such as AD FS may then choose to encrypt the&lt;br /&gt;
assertion, which this release cannot process. Configure the SP keystore and register the&lt;br /&gt;
generated metadata so the IdP sees a signing &#039;&#039;KeyDescriptor&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Do not disable both signature settings&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Setting &#039;&#039;signRequests&#039;&#039; and &#039;&#039;wantAssertionsSigned&#039;&#039; both to &#039;&#039;false&#039;&#039; is accepted but logs a&lt;br /&gt;
startup warning. In that state assertions are neither signed nor verified, which is not safe for&lt;br /&gt;
production.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SP-initiated flows and replay&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Obsidian issues an &#039;&#039;AuthnRequest&#039;&#039; and tracks its ID in the browser session for five minutes.&lt;br /&gt;
When the IdP&#039;s response carries &#039;&#039;InResponseTo&#039;&#039;, it must match one of those pending IDs and is&lt;br /&gt;
consumed on use, so the same response cannot be replayed. There is no persistent store of&lt;br /&gt;
processed assertion IDs, so keep assertion lifetimes short at the IdP.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Clustered deployments and IdP-initiated logout&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
An IdP-initiated &#039;&#039;LogoutRequest&#039;&#039; can only end the Obsidian session on the node that receives&lt;br /&gt;
it. Use sticky sessions if you rely on single logout across a cluster.&lt;br /&gt;
&lt;br /&gt;
===== Keycloak example =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.saml.SAMLAuthenticator&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.entityId=https://app.example.com/obsidian&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.assertionConsumerServiceUrl=https://app.example.com/obsidian/saml/callback&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.idpMetadataUrl=https://keycloak.example.com/realms/obsidian/protocol/saml/descriptor&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.path=/opt/obsidian/saml-sp.p12&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.password=changeit&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.alias=obsidian-sp&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.attribute.groups=groups&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.1.group=Obsidian-Admins&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.1.roleName=Admin&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.2.group=Obsidian-Write&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.2.roleName=Write&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.3.group=Obsidian-Read&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.3.roleName=LimitedRead&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.displayName=Keycloak&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.logout.mode=local&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.requireRole=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Provider notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Microsoft Entra ID&#039;&#039;&#039;: group claims are omitted from SAML assertions once an account belongs to&lt;br /&gt;
roughly 150 groups. Filter the groups sent to the application, or map app roles instead. Note this&lt;br /&gt;
threshold differs from the OIDC one described above.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AD FS&#039;&#039;&#039;: the federation metadata document is normally at&lt;br /&gt;
&#039;&#039;/FederationMetadata/2007-06/FederationMetadata.xml&#039;&#039;. Register signing-capable SP metadata so AD FS&lt;br /&gt;
does not encrypt the assertion, which Obsidian cannot decrypt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Okta&#039;&#039;&#039;: use the application&#039;s SAML metadata URL for &#039;&#039;idpMetadataUrl&#039;&#039;. Okta&#039;s logout endpoints&lt;br /&gt;
may be POST-only; if no HTTP-Redirect &#039;&#039;SingleLogoutService&#039;&#039; is advertised, &#039;&#039;logout.mode=slo&#039;&#039;&lt;br /&gt;
falls back to local logout.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auth0&#039;&#039;&#039;: federation metadata is served at &#039;&#039;samlp/metadata/&amp;lt;client-id&amp;gt;&#039;&#039;. Group memberships must&lt;br /&gt;
be added to the assertion with an Auth0 Action or rule.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Keycloak and other generic IdPs&#039;&#039;&#039;: Keycloak publishes a SAML descriptor at&lt;br /&gt;
&#039;&#039;/realms/&amp;lt;realm&amp;gt;/protocol/saml/descriptor&#039;&#039;. Any IdP that publishes SAML 2.0 metadata with an&lt;br /&gt;
HTTP-POST &#039;&#039;AssertionConsumerService&#039;&#039; target and a signing certificate will work.&lt;br /&gt;
&lt;br /&gt;
===== v1 limitations =====&lt;br /&gt;
&lt;br /&gt;
* The REST API is unavailable while &#039;&#039;SAMLAuthenticator&#039;&#039; is active — there is no Bearer or JWT equivalent.&lt;br /&gt;
* SP-initiated login only.&lt;br /&gt;
* Encrypted assertions are not supported.&lt;br /&gt;
* No persistent assertion replay store beyond the single-use &#039;&#039;InResponseTo&#039;&#039; match and the assertion time window.&lt;br /&gt;
* Dynamic role map (&#039;&#039;dynamicRole.prefix&#039;&#039;/&#039;&#039;suffix&#039;&#039;) requires a server restart when new root job folders are added.&lt;br /&gt;
* Installer UI supports up to 10 static role pairs; the runtime supports unlimited.&lt;br /&gt;
&lt;br /&gt;
=== SMTP Mail Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Email configuration for notifications, if desired&lt;br /&gt;
#for straight up open relay, just specify the host using&lt;br /&gt;
mail.smtp.host=smtp.myopenrelayhost.com&lt;br /&gt;
mail.smtp.port=port &lt;br /&gt;
#(standard ports are 25, 465 for SSL, 587 for TLS)&lt;br /&gt;
&lt;br /&gt;
#for using TLS and SSL, provide these as necessary&lt;br /&gt;
mail.smtp.socketFactory.port=port&lt;br /&gt;
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory&lt;br /&gt;
mail.smtp.auth=true&lt;br /&gt;
mail.smtp.user=myauthenticatedemailuser@email.com&lt;br /&gt;
mail.smtp.password=PASSWORD&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 2.5.0, we support JNDI lookups for email sessions. All other email properties can be excluded.&lt;br /&gt;
mail.session.jndi.path=java:comp/env/mail/session&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Database Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Database configuration&lt;br /&gt;
com.carfey.obsidian.db.url=jdbc:mysql://localhost/obsidian&lt;br /&gt;
com.carfey.obsidian.db.userId=user&lt;br /&gt;
com.carfey.obsidian.db.password=pass&lt;br /&gt;
&lt;br /&gt;
# Sample JDBC URL formats for all platforms&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:mysql://localhost:3306/obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:h2:C:/dev/workspace/obsidian;MVCC=TRUE&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:oracle:thin:@localhost:1521:obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:postgresql://localhost:5432/obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:sqlserver://localhost:1433;databaseName=obsidian&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 6.3.0, we support constructing simple JDBC urls from component properties&lt;br /&gt;
# Only supports basic URL formats noted above and does not support H2&lt;br /&gt;
#com.carfey.obsidian.db.host=localhost&lt;br /&gt;
#com.carfey.obsidian.db.port=3306&lt;br /&gt;
#com.carfey.obsidian.db.databaseName=obsidianDB&lt;br /&gt;
#com.carfey.obsidian.db.dbType=mariadb/mysql/oracle/postgresql/sqlserver&lt;br /&gt;
#com.carfey.obsidian.db.oracleSid=SIDORCL&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 2.5.0, we support JNDI lookups for database connectivity.&lt;br /&gt;
com.carfey.obsidian.db.url=java:comp/env/jdbc/obsidian&lt;br /&gt;
com.carfey.obsidian.db.jndiType=mysql&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=mysql is also used for MariaDB&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=oracle&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=postgresql&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=h2&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=sqlserver&lt;br /&gt;
&lt;br /&gt;
# Table prefix may be used to add a prefix to tables and related database objects. It must be 6 or fewer characters and can contain letters or underscores. Available as of version 2.0.&lt;br /&gt;
com.carfey.obsidian.db.tablePrefix=OBSDN_&lt;br /&gt;
&lt;br /&gt;
# For Oracle databases, when Obsidian&#039;s tables exist in a different schema from the user specified above, or if the user does not default to the schema matching its name, specify the target schema here. Available as of version 2.1.&lt;br /&gt;
# For details on the required privileges to run with an alternate user, see https://wiki.obsidianscheduler.com/docs/Obsidian_Tables#Oracle_Privileges&lt;br /&gt;
# This same configuration item can be used to configure the Postgresql schema. If unspecified, the default is public. Available as of Obsidian 2.1.1. &lt;br /&gt;
# If using JNDI and Oracle/Postgresql, we recommend you set this value to ensure best performance.&lt;br /&gt;
com.carfey.obsidian.db.schema=obsidian&lt;br /&gt;
&lt;br /&gt;
#default max connections per pool if not specified is 50&lt;br /&gt;
com.carfey.obsidian.db.maxConnections=40&lt;br /&gt;
&lt;br /&gt;
#default millis timeout to retrieve available connection from pool if not specified is 2000&lt;br /&gt;
com.carfey.obsidian.db.connectionTimeout=2000&lt;br /&gt;
&lt;br /&gt;
# As of 3.4.0, you can disable pooling of connections, which is suggested only if using another connection pool through JNDI. When true, maxConnections is ignored. &lt;br /&gt;
# When a JNDI data source is selected in the installer, this is set to true by default.&lt;br /&gt;
com.carfey.obsidian.db.disablePooling=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.3.0, Oracle database supports configured wait time.&lt;br /&gt;
com.carfey.obsidian.db.timedLockWaitEnabled=true (default)&lt;br /&gt;
com.carfey.obsidian.db.timedLockWaitSeconds=1 (default, prior to 4.3.0, indefinite wait)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Logging Properties ===&lt;br /&gt;
As of &#039;&#039;&#039;Obsidian 5.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Please see [https://logging.apache.org/log4j/2.x/manual/configuration.html Log4j2 configuration]&lt;br /&gt;
&lt;br /&gt;
Before &#039;&#039;&#039;Obsidian 5.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Please see [https://logging.apache.org/log4j/1.2/manual.html Log4j Configuration]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Miscellaneous Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# If you wish to change the tokens used to reference global parameters in your job configuration, use the following two values:&lt;br /&gt;
global.param.start.token={{&lt;br /&gt;
global.param.end.token=}}&lt;br /&gt;
&lt;br /&gt;
# Populated by the installer, these values can be used to initialize licence key information into the database.&lt;br /&gt;
com.carfey.obsidian.licence.key=licenceKey&lt;br /&gt;
# Name is only required for hardware-linked or site licenses. Use the name exactly as provided by Carfey Software Corporation in your license email.&lt;br /&gt;
com.carfey.obsidian.licence.name=licenceeName&lt;br /&gt;
&lt;br /&gt;
# As of 2.8.0, if you have issues with classloading (e.g. in Grails or Spring), this flag enables usage of the context classloader .&lt;br /&gt;
com.carfey.jdk.useContextClassLoader=true&lt;br /&gt;
&lt;br /&gt;
# As of 2.9.0, you can set the scheduler host designator name multiple times in the same JVM by using this parameter:&lt;br /&gt;
com.carfey.obsidian.schedulerDesignation=obsidian-dev&lt;br /&gt;
&lt;br /&gt;
# As of 3.0.0, Job Forking can be enabled and configured per node&lt;br /&gt;
com.carfey.obsidian.jvmJobForkingEnabledOnThisNode=true&lt;br /&gt;
# This property is the location of the fork scripts. Obsidian is bundled with obsidianForkedJob.bat and obsidianForkedJob.sh. These are the expected script names.&lt;br /&gt;
com.carfey.obsidian.forkedJobScriptLocation=/Obsidian-3.0.0&lt;br /&gt;
# Use the following optional property if you need to override the default classpath that is built using the contents of the standalone directory.&lt;br /&gt;
# This allows for job forking support in embedded and even webapp deployments. Use the classpath format supported by your operating system.&lt;br /&gt;
#com.carfey.obsidian.forkedJobscriptClasspathOverride=&lt;br /&gt;
# Use the following optional property if you wish to include database configuration parameters as arguments passed to the forking scripts and the target forked Obsidian class.&lt;br /&gt;
# For example, you may need to do this if your configuration is done via System Property overrides or programmmatically in your embedded Obsidian instance.&lt;br /&gt;
#com.carfey.obsidian.jvmJobForkingIncludeDbSysParms=true&lt;br /&gt;
&lt;br /&gt;
# Obsolete as of 4.0.0. As of 3.4.0, the base URL used to resolve links in the Obsidian web app can be overridden. This is typically only required when a load balancer or proxy is used to access Obsidian.&lt;br /&gt;
com.carfey.obsidian.baseHrefUrl=http://myhost:8080/obsidian&lt;br /&gt;
&lt;br /&gt;
# As of 4.3.0, description/parameter formatting is supported. Custom formatter (com.carfey.ops.job.config.formatter.Formatter) supported via this parameter.&lt;br /&gt;
com.carfey.obsidian.formatterClass=com.carfey.ops.job.config.formatter.MarkdownFormatter (default)&lt;br /&gt;
&lt;br /&gt;
# As of 4.4.0, support for running DDL outside Obsidian and applying only the data portion of upgrades. May require incremental upgrades. Contact support for assistance. Enabled with the following:&lt;br /&gt;
com.carfey.obsidian.runner.skipDDL=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.5.0, Slack notifications can be automatically configured for FATAL, ERROR and WARN events by setting this property with a Slack incoming web hook URL. The user name property is optional and defaults to Obsidian Scheduler&lt;br /&gt;
com.carfey.obsidian.slack.webhookUrl=https://hooks.slack.com/services/XXXXXXX/YYYYYYYY/ZZZZZZZZZZ&lt;br /&gt;
com.carfey.obsidian.slack.userName=Obsidian Scheduler (prod)&lt;br /&gt;
&lt;br /&gt;
# As of 4.6.0, Obsidian supports starting a node in paused state. This can also be set as a System property.&lt;br /&gt;
com.carfey.obsidian.schedulerPausedOnStartup=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.10.0, Obsidian supports disabling script engine jobs selectively.&lt;br /&gt;
com.carfey.ops.job.script.BeanShellJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.GroovyJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.RubyJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.PythonJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.JavaScriptJob.enabled=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.0.0, Signal interrupt (supported as of 4.5.1) is disabled by default. Use configuration to enable.&lt;br /&gt;
com.carfey.obsidian.signalHandlerActiveForScheduler=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.0, you can send Obsidian events to a REST endpoint via an out-of-the-box Event Hook. The basicAuthorization property is optional, the sample showing its use for myusername:mypassword. The &amp;quot;Basic &amp;quot; prefix is added by Obsidian.&lt;br /&gt;
com.carfey.obsidian.restfulEventHook.url=https://myendpoint.com:1234/rest/obsidian_events&lt;br /&gt;
com.carfey.obsidian.restfulEventHook.basicAuthorization=bXl1c2VybmFtZTpteXBhc3N3b3Jk&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.1, maintenance jobs are automatically scheduled for new installations. Set the following property to disable this behaviour.&lt;br /&gt;
com.carfey.obsidian.skipMaintenanceJobInitialization=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.1, you can send Obsidian events to standard output and error streams using an out-of-the-box Event Hook. You can enable the default behaviour and auto-register it by adding the following configuration.&lt;br /&gt;
com.carfey.obsidian.standardOutputStreamsEventHook.enabled=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Table Prefixes ===&lt;br /&gt;
As shown in full configuration reference, a table name prefix may be specified to create Obsidian tables with names beginning with a specified string.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Table prefix may be used to add a prefix to tables and related database objects. It must be 6 or fewer characters and can contain letters or underscores. Available as of version 2.0.&lt;br /&gt;
com.carfey.obsidian.db.tablePrefix=OBSDN_&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Properties/Yaml File ==&lt;br /&gt;
&lt;br /&gt;
The above configuration must reside in a properties file named &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; or a yaml file named &amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; found according to the rules of [http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String) ClassLoader.getResource].  If both yaml and properties files are found, yaml is loaded first and properties values will override. Some possibilities include &amp;lt;code&amp;gt;WEB-INF/classes/com.carfey.(properties/yaml)&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;com.carfey.(properties/yaml)&amp;lt;/code&amp;gt; file in a directory which is explicitly added to the classpath, or at the root of a jar file as is done in &amp;lt;code&amp;gt;obsidian-props.jar&amp;lt;/code&amp;gt; for standalone deployments. Prior to Obsidian 3.6, this file had to exist on the classpath, even if only using the override options below to configure Obsidian.&lt;br /&gt;
&lt;br /&gt;
Instead of using a classpath resource for properties, you can specify an external properties file using the Java system property &amp;lt;code&amp;gt;carfey.properties.file&amp;lt;/code&amp;gt;. All expected properties must be specified in either the default &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; file on the classpath or in the override file. Any properties found in both files will use the override file&#039;s values. Usage: &amp;lt;code&amp;gt;-Dcarfey.properties.file=/home/obsidian/obsidian.properties&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Instead of using a classpath resource for yaml, you can specify an external yaml file using the Java system property &amp;lt;code&amp;gt;carfey.yaml.file&amp;lt;/code&amp;gt;. All expected configuration values must be specified in either the default &amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; file on the classpath or in the override file. Any configuration values found in both files will use the override file&#039;s values. Usage: &amp;lt;code&amp;gt;-Dcarfey.yaml.file=/home/obsidian/obsidian.yaml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 2.1, you may also use a programmatic properties override. Simply call &amp;lt;code&amp;gt;com.carfey.jdk.sys.Configurator.setOverride(Properties props)&amp;lt;/code&amp;gt; as early as possible in the application startup.  To quote the javadoc of this method, &#039;&#039;For use as a programmatic properties override. This must be called before any classes are accessed either through invocation or class initialization that may require access to configuration done through properties. Best if done as early as possible in code, perhaps first in an entry point class that does little else and then hands-off to existing entry point.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 4.4, you may specify configuration values as System Properties or Environment Variables. Duplicated values are resolved as follows:&lt;br /&gt;
# System Property&lt;br /&gt;
# Environment Variable - &#039;&#039;&#039;&#039;&#039;overridden by System Property&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
# Properties - &#039;&#039;&#039;&#039;&#039;overridden by Environment Variable and System Property&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Notifications Configuration ==&lt;br /&gt;
&lt;br /&gt;
Sending notifications requires SMTP configuration to be defined in the Obsidian properties file. The properties file reference at the beginning of this page includes SMTP configuration details.&lt;br /&gt;
&lt;br /&gt;
In addition to the properties file, you can configure some Notifications settings to tweak how your emails are sent. These values are configurable under the &#039;&#039;Dispatch&#039;&#039; category of the [[Admin Scheduler Settings|scheduler settings]] screen. Defaults are provided, but you can update them appropriately for your needs.  &lt;br /&gt;
&lt;br /&gt;
=== Notification sender address and DMARC ===&lt;br /&gt;
&lt;br /&gt;
The notificationFromEmail scheduler setting controls the From address used for notification emails. It is available under:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Scheduler Settings → Dispatch → notificationFromEmail&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Set this to an address belonging to your organization and authorized by the SMTP service configured for your Obsidian installation.&lt;br /&gt;
&lt;br /&gt;
Do not use ops-notification@obsidianscheduler.com when sending through your own SMTP infrastructure. Your SMTP server normally cannot authenticate mail for the obsidianscheduler.com domain. This can cause SPF, DKIM, and DMARC failures and may result in notifications being quarantined or rejected.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
scheduler-notifications@example.com&lt;br /&gt;
&lt;br /&gt;
After updating the value, trigger a test notification and confirm successful receipt.&lt;br /&gt;
&lt;br /&gt;
[[File:NotifSettings_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
== Dependent Libraries ==&lt;br /&gt;
&lt;br /&gt;
Obsidian Scheduler requires a number of third party libraries, both for the web administration application and the scheduler itself.  Below is information on these libraries and how they are used.  Unless otherwise noted, they are mandatory. As of Obsidian 2.5.0, our installer supports selective [[Installation_Guide#Configuring_3rd_Party_Library_Conflict_Management|conflict management]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Authentication (OAuth / OIDC)&#039;&#039;&lt;br /&gt;
All libraries in this section are only required when OAuthAuthenticator is active. &#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* oauth2-oidc-sdk-11.23.1.jar, nimbus-jose-jwt-10.0.2.jar. Nimbus OAuth 2.0 SDK with OpenID Connect extensions and JOSE+JWT support.&lt;br /&gt;
* content-type-2.3.jar, lang-tag-1.7.jar. Supporting libraries for the Nimbus SDK.&lt;br /&gt;
* json-smart-2.5.2.jar, accessors-smart-2.5.2.jar. JSON parsing for the Nimbus SDK.&lt;br /&gt;
* jcip-annotations-1.0-1.jar. Concurrency annotations for the Nimbus SDK.&lt;br /&gt;
* asm-9.7.1.jar. Bytecode manipulation library; transitive dependency of the Nimbus SDK.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Authentication (SAML 2.0)&#039;&#039;&lt;br /&gt;
All libraries in this section are only required when SAMLAuthenticator is active; the installer removes them when SAML is not the selected authentication type. &#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* opensaml-core-4.3.2.jar, opensaml-saml-api-4.3.2.jar, opensaml-saml-impl-4.3.2.jar, opensaml-security-api-4.3.2.jar, opensaml-security-impl-4.3.2.jar, opensaml-xmlsec-api-4.3.2.jar, opensaml-xmlsec-impl-4.3.2.jar, opensaml-messaging-api-4.3.2.jar, opensaml-profile-api-4.3.2.jar, opensaml-soap-api-4.3.2.jar, opensaml-soap-impl-4.3.2.jar, opensaml-storage-api-4.3.2.jar. OpenSAML 4 modules implementing SAML 2.0 messages, metadata and signature handling.&lt;br /&gt;
* java-support-8.4.2.jar. Shibboleth utility library required by OpenSAML.&lt;br /&gt;
* xmlsec-2.3.4.jar. Apache Santuario XML Signature implementation.&lt;br /&gt;
* cryptacular-1.2.5.jar, bcprov-jdk18on-1.85.jar, bcpkix-jdk18on-1.85.jar, bcutil-jdk18on-1.85.jar. Cryptographic providers used for assertion signature verification and SP key handling.&lt;br /&gt;
* velocity-engine-core-2.3.jar. Template engine used by OpenSAML&#039;s message encoders.&lt;br /&gt;
* commons-lang3-3.20.0.jar, commons-codec-1.15.jar. Apache Commons utilities required by OpenSAML.&lt;br /&gt;
* httpclient-4.5.14.jar, httpcore-4.4.16.jar. HTTP client used to retrieve IdP federation metadata.&lt;br /&gt;
* guava-32.1.3-jre.jar, failureaccess-1.0.1.jar, listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar. Guava collections and its companion placeholder artifacts, required by OpenSAML.&lt;br /&gt;
* metrics-core-4.2.25.jar. Dropwizard Metrics, required by OpenSAML.&lt;br /&gt;
* slf4j-api-2.0.12.jar, log4j-slf4j2-impl-2.26.0.jar. SLF4J API and its log4j2 binding, so OpenSAML&#039;s diagnostic logging is routed into Obsidian&#039;s log4j2 configuration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Embedded Tomcat (Obsidian Embedded Tomcat JAR packs only)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* tomcat-embed-core-10.1.39.jar, tomcat-embed-el-10.1.39.jar — Apache Tomcat embedded components (Apache License 2.0). Present in &#039;&#039;&#039;embed-tomcat-lib/&#039;&#039;&#039; when an Embedded Tomcat pack is selected.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Common&#039;&#039;&lt;br /&gt;
* obsidian.jar.  Core Obsidian lib.&lt;br /&gt;
* gson-2.10.1. Supports REST API and web administration.&lt;br /&gt;
* jmustache-1.15.jar. Provides email templating.&lt;br /&gt;
* log4j-api-2.26.0.jar, log4j-core-2.26.0.jar.  Obsidian logging uses log4j2.&lt;br /&gt;
* snakeyaml-2.3.jar. Support for YAML configuration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Databases&#039;&#039;&lt;br /&gt;
* h2-1.4.200.jar.  H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* mariadb-java-client-2.7.2.jar. MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* mssql-jdbc-9.2.1.jre11.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc11-21.1.0.0.jar. Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-42.2.19.jar.  PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Mail&#039;&#039; &lt;br /&gt;
* activation-1.1.jar, javax.mail-1.6.2.jar. Used for javamail (javax) email notifications.&lt;br /&gt;
* angus-activation-2.0.1.jar, angus-mail-2.0.2.jar. Optionally used with Jakarta EE email notifications.&lt;br /&gt;
* jakarta.activation-api-2.1.2.jar, jakarta.mail-api-2.1.2.jar. Used for Jakarta EE email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Scripts&#039;&#039;&lt;br /&gt;
* bsh-2.0b5.jar.  Support for our Bean Shell script execution. (http://www.beanshell.org).  Only required for BeanShellJob usage.&lt;br /&gt;
* jruby-9.2.17.0.jar. Support for Ruby script execution (https://www.jruby.org/). Only required for RubyJob usage.&lt;br /&gt;
* jython-standalone-2.7.2.jar. Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Scripts - Prior to Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Groovy scripting shipped as a single uber archive: apache-groovy-all-4.0.24.jar&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Scripts - As of Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Groovy scripting ships as 13 modular JARs: groovy-4.0.24.jar, groovy-astbuilder-4.0.24.jar, groovy-datetime-4.0.24.jar, groovy-dateutil-4.0.24.jar, groovy-json-4.0.24.jar, groovy-jsr223-4.0.24.jar, groovy-macro-4.0.24.jar, groovy-macro-library-4.0.24.jar, groovy-nio-4.0.24.jar, groovy-sql-4.0.24.jar, groovy-templates-4.0.24.jar, groovy-typecheckers-4.0.24.jar, groovy-xml-4.0.24.jar.&lt;br /&gt;
Plus four owned transitives (included when Groovy is enabled): asm-analysis-9.7.1.jar, asm-tree-9.7.1.jar, asm-util-9.7.1.jar, javaparser-core-3.26.2.jar&lt;br /&gt;
All 17 JARs above are excluded when Groovy scripting is disabled at install time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Web Admin&#039;&#039;&lt;br /&gt;
* dom4j-2.1.3.jar.  XML utilities.&lt;br /&gt;
* flexmark-0.62.2.jar, flexmark-util-0.62.2.jar. Markdown formatting for Description and Parameter job annotations.&lt;br /&gt;
* jxl-2.6.12.jar. Excel file format utilities. Only required in web administration.&lt;br /&gt;
* opencsv-5.4.jar. Support for CSV downloads in UI.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Web Admin - As of Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Markdown rendering for job and parameter descriptions ships as 12 modular JARs:&lt;br /&gt;
flexmark-0.62.2.jar, flexmark-util-ast-0.62.2.jar, flexmark-util-builder-0.62.2.jar, flexmark-util-collection-0.62.2.jar, flexmark-util-data-0.62.2.jar, flexmark-util-dependency-0.62.2.jar, flexmark-util-format-0.62.2.jar, flexmark-util-html-0.62.2.jar, flexmark-util-misc-0.62.2.jar, flexmark-util-options-0.62.2.jar, flexmark-util-sequence-0.62.2.jar, flexmark-util-visitor-0.62.2.jar&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;, the following changes were made:&lt;br /&gt;
* &#039;&#039;&#039;log4j upgraded&#039;&#039;&#039;: log4j-api and log4j-core updated from 2.17.1 to 2.26.0.&lt;br /&gt;
* &#039;&#039;&#039;Modular Groovy libraries&#039;&#039;&#039;: Obsidian no longer ships &#039;&#039;&#039;apache-groovy-all-4.0.24.jar&#039;&#039;&#039;. Groovy scripting now uses 13 modular JARs (see &#039;&#039;Scripts&#039;&#039; above). When Groovy is disabled at install time, all 13 modules are excluded.&lt;br /&gt;
* &#039;&#039;&#039;Modular Flexmark libraries&#039;&#039;&#039;: Obsidian no longer ships &#039;&#039;&#039;flexmark-util-0.62.2.jar&#039;&#039;&#039;. Markdown rendering for job and parameter descriptions now uses 12 modular Flexmark JARs (see &#039;&#039;Web Admin&#039;&#039; above).&lt;br /&gt;
* &#039;&#039;&#039;OAuth / OIDC authentication libraries&#039;&#039;&#039; added (Nimbus OAuth 2.0 SDK and transitive dependencies). Required only when OAuthAuthenticator is configured. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties|Authentication (OAuth/OIDC)]] above.&lt;br /&gt;
* &#039;&#039;&#039;SAML 2.0 authentication libraries&#039;&#039;&#039; added (OpenSAML 4 and its transitive dependencies, plus the SLF4J-to-log4j2 binding). Required only when SAMLAuthenticator is configured. See [[Advanced_Configuration#SAML_Authentication_Properties|Authentication (SAML 2.0)]] above.&lt;br /&gt;
* Embedded Tomcat runtime added (tomcat-embed-core-10.1.39.jar, tomcat-embed-el-10.1.39.jar). Present only when an Embedded Tomcat pack is selected. Replaces the Jetty quick-start distribution which has been removed.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 7.0.0&#039;&#039;&#039;, the following libraries were in use.&lt;br /&gt;
* log4j-api-2.17.1.jar, log4j-core-2.17.1.jar.  Obsidian logging uses log4j2.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 6.0.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* groovy-all-3.0.7.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.4.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* gson-2.8.6.jar. Supports REST API and web administration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.2.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* jakarta.activation-1.2.1.jar. Used for email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.0.3&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* &#039;&#039;Obsidian 5.0.2&#039;&#039; log4j-api-2.16.0.jar, log4j-core-2.16.0.jar.  &lt;br /&gt;
* &#039;&#039;Obsidian 5.0.1&#039;&#039; log4j-api-2.15.0.jar, log4j-core-2.15.0.jar.  &lt;br /&gt;
* &#039;&#039;Obsidian 5.0.0&#039;&#039; log4j-api-2.14.1.jar, log4j-core-2.14.1.jar.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.0.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* activation-1.1.jar, javax.mail-1.5.5.jar.  Used for email notifications.&lt;br /&gt;
* dom4j-1.6.1.jar. XML utilities.&lt;br /&gt;
* gson-2.7.jar. Supports REST API and web administration.&lt;br /&gt;
* bsh-2.0b4.jar.  Support for our Bean Shell script execution. (http://www.beanshell.org).  Only required for BeanShellJob usage.&lt;br /&gt;
* groovy-all-2.4.14.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
* jython-standalone-2.7.0.jar. Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
* jruby-complete-9.2.7.0.jar. Support for Ruby script execution (https://www.jruby.org/). Only required for RubyJob usage.&lt;br /&gt;
* jmustache-1.12.jar. Provides email templating.&lt;br /&gt;
* h2-1.4.192.  H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* log4j-1.2.9.jar.  Obsidian logging uses log4j.&lt;br /&gt;
* mariadb-java-client-1.4.5.jar.  MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* jtds-1.3.1.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc7-12.1.0.1.jar.  Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-9.4.1208.jre7.jar. PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
* opencsv-3.8.jar. Support for CSV downloads in UI.&lt;br /&gt;
* flexmark-0.19.6.jar, flexmark-util-0.19.6.jar (as of 4.3.0). Markdown formatting for Description and Parameter job annotations.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 4.5.1&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* groovy-all-2.4.7.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 4.0.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* javax.mail-1.5.2.jar.  Used for email notifications.&lt;br /&gt;
* jstl.jar, standard.jar.  Web utilities. Only required in web administration.&lt;br /&gt;
* gson-2.2.2.jar. Supports REST API and web administration.&lt;br /&gt;
* groovy-all-2.1.8.jar.  Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
* jython-standalone-2.5.3.jar.  Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
* jmustache-1.8.jar. Provides email templating.&lt;br /&gt;
* h2-1.3.154. H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* mariadb-java-client-1.1.5.jar. MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* jtds-1.2.8.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc6-11.2.0.3.jar. Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-9.0-801.jdbc4.jar.  PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.9.1&#039;&#039;&#039;, the following redundant library was in included in the Obsidian packaging.&lt;br /&gt;
* smtp.jar.  Used for email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.2.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* groovy-all-1.7.6.jar&lt;br /&gt;
* jython.jar (version 2.5.2rc2)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.1.1&#039;&#039;&#039;, the following libraries were also included.&lt;br /&gt;
* carfey-date-1.2.jar or carfey-date-1.1.jar.  Date math/manipulation.&lt;br /&gt;
* jdk-gen.jar, jdk.jar, suite-gen.jar, suite.jar.  Core Obsidian libs.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.1.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* ojdbc14.jar Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 1.5&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* gson-1.5.jar. Supports REST API and web administration.&lt;br /&gt;
&lt;br /&gt;
=== Required Libraries for Embedded API ===&lt;br /&gt;
&lt;br /&gt;
To use the [[Embedded_API|Embedded API]], the following resources should be imported. If you have newer versions of the same JARs in your application, feel free to use the newer version. The JAR files below can generally be found in the &#039;&#039;standalone&#039;&#039; directory if your installation. The &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; file can be extracted from the &amp;lt;code&amp;gt;obsidian-properties-configuration.jar&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;obsidian-yaml-configuration.jar&amp;lt;/code&amp;gt; in the &#039;&#039;standalone&#039;&#039; directory, or from an Obsidian WAR file under &amp;lt;code&amp;gt;WEB-INF/classes&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* com.carfey.properties/com.carfey.yaml (configuration file), or obsidian-properties-configuration.jar/obsidian-yaml-configuration.jar, which contains the configuration file&lt;br /&gt;
* obsidian.jar&lt;br /&gt;
* dom4j-2.1.3.jar&lt;br /&gt;
* flexmark-0.62.2.jar plus the modular Flexmark JARs listed under &#039;&#039;Web Admin&#039;&#039; above&lt;br /&gt;
* gson-2.10.1&lt;br /&gt;
* log4j-api-2.26.0.jar, log4j-core-2.26.0.jar&lt;br /&gt;
* Appropriate JDBC JAR (e.g. mariadb-java-client-2.7.2.jar for MySQL or MariaDB)&lt;br /&gt;
&lt;br /&gt;
In addition, the following resources should be included if you wish to have notification support enabled when using the API:&lt;br /&gt;
* jmustache-1.15.jar&lt;br /&gt;
** jakarta.activation-api-2.1.2.jar, jakarta.mail-api-2.1.2.jar. Used for Jakarta EE email notifications.&lt;br /&gt;
** angus-activation-2.0.1.jar, angus-mail-2.0.2.jar. Optionally used with Jakarta EE email notifications.&lt;br /&gt;
** activation-1.1.jar, javax.mail-1.6.2.jar. Used for javamail (javax) email notifications.&lt;br /&gt;
&lt;br /&gt;
Finally, if you need to configure scripting jobs from the API, you will need to include any applicable JARs from the following list:&lt;br /&gt;
* apache-groovy-all-4.0.24.jar&lt;br /&gt;
* bsh-2.0b5.jar&lt;br /&gt;
* jruby-9.2.17.0.jar&lt;br /&gt;
* jython-standalone-2.7.2.jar&lt;br /&gt;
&lt;br /&gt;
== Disabling Job Execution &amp;amp; Scheduling in the Web Application ==&lt;br /&gt;
See the instructions within the [[Getting_Started#Disabling_Job_Scheduling_in_the_Web_Application|Getting Started Guide]].&lt;br /&gt;
&lt;br /&gt;
== Disabling Automatic Database Updates ==&lt;br /&gt;
See the instructions within the [[Getting_Started#Disabling_Automatic_Database_Updates|Getting Started Guide]].&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Advanced_Configuration&amp;diff=4132</id>
		<title>Advanced Configuration</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Advanced_Configuration&amp;diff=4132"/>
		<updated>2026-07-31T02:16:12Z</updated>

		<summary type="html">&lt;p&gt;Craig: /* Dependent Libraries */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian requires some initial configuration parameters for database connection information and desired authentication mechanisms.  The installer takes care of configuring these as provided.  For your reference should you wish to manually create/edit these, note the details below.&lt;br /&gt;
&lt;br /&gt;
== Configuration Details ==&lt;br /&gt;
&lt;br /&gt;
The following sections show the available properties and sample values that can be used in the &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; properties file. While properties format is shown, all the same configuration items apply to yaml support. See [[#Properties.2FYaml_File|Properties/Yaml File]] for more details on this file.&lt;br /&gt;
&lt;br /&gt;
=== Authentication Properties ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Comment out the native authenticator setup&lt;br /&gt;
#com.carfey.suite.security.Authenticator=com.carfey.suite.security.DBAuthenticator&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LDAP Authentication Properties ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#Set your LDAP info here&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.LdapAuthenticator&lt;br /&gt;
# As of Obsidian 2.1.1, you can specify the attribute type used in building up the distinguished name (dn). If unspecified, defaults to cn&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.dn.attribute=uid&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of dn.bases to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.dn.base=ou=people,o=MyOrgHere&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.url=ldap://localhost:10389&lt;br /&gt;
# As of Obsidian 2.1.1, you can specify the SECURITY_AUTHENTICATION. Defaults to simple. &lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.securityAuthentication=simple&lt;br /&gt;
# Any necessary additional information such as Provider, Principal and Host will need to be handled in your custom class extending LdapAuthenticator.&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 3.5.1, you can login via an LDAP attribute that is not part of the dn. A matching entry is found in the LDAP Directory (using anonymous or a fixed lookup account) &lt;br /&gt;
# after which the dn attribute above is retrieved to perform the user&#039;s authentication. The following 5 attributes are only used for this type of lookup and authentication.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.lookupDnAttribute=false&lt;br /&gt;
# As of Obsidian 3.5.1, this is the attribute name that is being searched for in the LDAP directory to build the dn for eventual authentication.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.loginAttribute=sAMAccountName&lt;br /&gt;
# As of Obsidian 3.5.1, this determines if these lookups will be done anonymously (without a session authenticated by user/password)&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.anonymousEnabled=false&lt;br /&gt;
# As of Obsidian 3.5.1, if anonymous lookups are not permitted or desired, provide the dn and password to be used for lookups. Typically, a read-only account with &lt;br /&gt;
# read rights to the LDAP entries and attributes in question is sufficient.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchUserFullDn=cn=ObsidianLDAPReadOnly,ou=people,o=MyOrgHere&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchPassword=password&lt;br /&gt;
# As of Obsidian 4.5.1, an alternate search base configuration parameter is used for the searchUser&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchUserDnBase=ou=people,o=MyOrgHere&lt;br /&gt;
# As of Obsidian 4.7.1, you may specify one or more additional group membership attributes beyond the defaults&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.groupMembershipAttribute=businessGroup~~~departmentName&lt;br /&gt;
&lt;br /&gt;
# Configure who may access the web app, based on LDAP group membership&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.accessDN=cn=SchedulerAccess,ou=groups,o=MyOrgHere&lt;br /&gt;
&lt;br /&gt;
# Configure the Write role used in the admin web app, based on LDAP group membership (by default, users may only read)&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.write.dn=cn=SchedulerWrite,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.write.roleName=Write&lt;br /&gt;
&lt;br /&gt;
# Configure the Admin role used in the admin web app, based on LDAP group membership (users may configure system parameters, etc).&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.admin.dn=cn=SchedulerAdmin,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.admin.roleName=Admin&lt;br /&gt;
&lt;br /&gt;
# Configure the LimitedRead role used in the admin web app, based on LDAP group membership (by default, users may only read)&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.limitedRead.dn=cn=SchedulerLimitedRead,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.limitedRead.roleName=LimitedRead&lt;br /&gt;
&lt;br /&gt;
# Configure the APIrole used by the REST API, based on LDAP group membership&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.api.dn=cn=SchedulerAPI,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.api.roleName=API&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OAuth / OIDC Authentication Properties ====&lt;br /&gt;
&lt;br /&gt;
To use OAuth / OIDC SSO, set the &#039;&#039;Authenticator&#039;&#039; property to&lt;br /&gt;
&#039;&#039;com.carfey.suite.security.OAuthAuthenticator&#039;&#039; and add the properties below. All&lt;br /&gt;
properties are prefixed with &#039;&#039;com.carfey.suite.security.OAuthAuthenticator.&#039;&#039; (shown&lt;br /&gt;
without prefix in the table for brevity).&lt;br /&gt;
&lt;br /&gt;
===== Required properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;issuer&#039;&#039; || OIDC Issuer URL. Obsidian fetches &#039;&#039;&amp;lt;issuer&amp;gt;/.well-known/openid-configuration&#039;&#039; lazily on first use and caches it.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clientId&#039;&#039; || Client ID registered at the IdP.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clientSecret&#039;&#039; || Client secret. Keep out of version control.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;redirectUri&#039;&#039; || Exact callback URL registered at the IdP (e.g. &#039;&#039;https://app.example.com/oauth/callback&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;scopes&#039;&#039; || Space-separated OAuth scopes (e.g. &#039;&#039;openid profile email groups&#039;&#039;). Must include &#039;&#039;openid&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groups&#039;&#039; || Name of the token claim that carries group memberships (e.g. &#039;&#039;groups&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.group&#039;&#039; || IdP group value for this mapping. &#039;&#039;&amp;lt;idx&amp;gt;&#039;&#039; is an arbitrary stable label (numeric or descriptive).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.roleName&#039;&#039; || Obsidian role for this mapping. At least one &#039;&#039;Admin&#039;&#039; mapping is required. Valid roles: &#039;&#039;&#039;Admin&#039;&#039;&#039;, &#039;&#039;&#039;API&#039;&#039;&#039;, &#039;&#039;&#039;Write&#039;&#039;&#039;, &#039;&#039;&#039;LimitedRead&#039;&#039;&#039;, &#039;&#039;&#039;Operator&#039;&#039;&#039;, &#039;&#039;&#039;Author&#039;&#039;&#039;, and folder-based roles (&#039;&#039;&amp;lt;folder&amp;gt;-(Write|Author|Operator)&#039;&#039;).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Optional properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Default !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;displayName&#039;&#039; || (none) || Label on the SSO button: &amp;quot;Sign in with &#039;&#039;&amp;lt;displayName&amp;gt;&#039;&#039;&amp;quot;. Defaults to &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;logout.mode&#039;&#039; || &#039;&#039;local&#039;&#039; || &#039;&#039;local&#039;&#039;: invalidates Obsidian session only. &#039;&#039;rp&#039;&#039;: RP-initiated logout via IdP &#039;&#039;end_session_endpoint&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;postLogoutRedirectUri&#039;&#039; || (none) || Where to redirect after RP-initiated logout. Ignored when &#039;&#039;logout.mode=local&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.user&#039;&#039; || &#039;&#039;sub&#039;&#039; || Claim used as the internal user ID in audit logs. &#039;&#039;&#039;Strongly recommend keeping as &#039;&#039;sub&#039;&#039;&#039;&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.displayName&#039;&#039; || &#039;&#039;preferred_username&#039;&#039; || Claim shown in the Obsidian UI nav bar. Separate from &#039;&#039;claim.user&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.validateTyp&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, rejects Bearer tokens where &#039;&#039;typ&#039;&#039; is present but not &#039;&#039;at+JWT&#039;&#039;. Set &#039;&#039;false&#039;&#039; only for legacy IdPs that omit &#039;&#039;typ&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clockSkewSeconds&#039;&#039; || &#039;&#039;60&#039;&#039; || Clock skew tolerance for &#039;&#039;exp&#039;&#039;/&#039;&#039;nbf&#039;&#039; validation (seconds).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;metadata.ttlHours&#039;&#039; || &#039;&#039;24&#039;&#039; || OIDC discovery document cache TTL in hours.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;expectedAccessTokenAudience&#039;&#039; || &#039;&#039;clientId&#039;&#039; || Expected &#039;&#039;aud&#039;&#039; for Bearer access tokens. See security note below.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsSource&#039;&#039; || &#039;&#039;idtoken&#039;&#039; || Where to read groups during browser SSO: &#039;&#039;idtoken&#039;&#039;, &#039;&#039;accesstoken&#039;&#039;, or &#039;&#039;userinfo&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsBearerSource&#039;&#039; || &#039;&#039;accesstoken&#039;&#039; || Where to read groups on the Bearer path: &#039;&#039;idtoken&#039;&#039; or &#039;&#039;accesstoken&#039;&#039;. &#039;&#039;&#039;userinfo is not permitted on the Bearer path.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsValuePath&#039;&#039; || (none) || Sub-field name when groups is an array of objects (e.g. &#039;&#039;name&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsStripPrefix&#039;&#039; || (none) || Prefix to strip from each group value (e.g. &#039;&#039;/&#039;&#039; for Keycloak full-path style).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsUserinfoTimeout&#039;&#039; || &#039;&#039;5&#039;&#039; || HTTP timeout in seconds for the userinfo endpoint. Only used when &#039;&#039;claim.groupsSource=userinfo&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.prefix&#039;&#039; || (none) || Prefix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.suffix&#039;&#039;. Map is rebuilt at startup; adding a new root folder requires a restart.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.suffix&#039;&#039; || (none) || Suffix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.prefix&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;requireRole&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, browser SSO fails if IdP groups map to no Obsidian roles. Does not apply to REST Bearer tokens.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Security notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;expectedAccessTokenAudience and cross-JWT confusion&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When &#039;&#039;expectedAccessTokenAudience&#039;&#039; is not set it defaults to &#039;&#039;clientId&#039;&#039;. In many OIDC&lt;br /&gt;
configurations the id_token also carries &#039;&#039;aud=clientId&#039;&#039;, which could allow an id_token&lt;br /&gt;
to be submitted as a Bearer token. The &#039;&#039;claim.validateTyp=true&#039;&#039; default mitigates this&lt;br /&gt;
for compliant IdPs (access tokens carry &#039;&#039;typ=at+JWT&#039;&#039;; id_tokens carry &#039;&#039;typ=JWT&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
For maximum defence, set &#039;&#039;expectedAccessTokenAudience&#039;&#039; to a distinct resource-server&lt;br /&gt;
identifier if your IdP supports it.&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;claim.validateTyp=false&#039;&#039; AND &#039;&#039;expectedAccessTokenAudience&#039;&#039; is not explicitly set,&lt;br /&gt;
Obsidian logs a mandatory startup WARN advising you to set &#039;&#039;expectedAccessTokenAudience&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
===== Keycloak example =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.OAuthAuthenticator&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.issuer=http://localhost:8081/realms/obsidian&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.clientId=obsidian-client&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.clientSecret=test-client-secret-local&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.redirectUri=http://localhost:8080/oauth/callback&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.scopes=openid profile email groups&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.claim.groups=groups&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.claim.groupsSource=idtoken&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.1.group=Obsidian-Admins&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.1.roleName=Admin&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.2.group=Obsidian-Write&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.2.roleName=Write&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.3.group=Obsidian-Read&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.3.roleName=LimitedRead&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.4.group=Obsidian-API&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.4.roleName=API&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.displayName=Keycloak&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.logout.mode=local&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.requireRole=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Provider notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Microsoft Entra ID&#039;&#039;&#039;: groups claim contains GUIDs by default; configure Optional&lt;br /&gt;
Claims for display names. Accounts with 200+ groups trigger overage. Multi-tenant apps not&lt;br /&gt;
supported in v1.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auth0&#039;&#039;&#039;: opaque access tokens (Auth0 default) are not supported on the Bearer path.&lt;br /&gt;
Configure a custom API audience to get JWT access tokens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Okta&#039;&#039;&#039;: use the Custom Authorization Server URL, not the Org Authorization Server.&lt;br /&gt;
&lt;br /&gt;
===== v1 limitations =====&lt;br /&gt;
&lt;br /&gt;
* Opaque access tokens not supported on the REST/Bearer path.&lt;br /&gt;
* No &#039;&#039;jti&#039;&#039; replay protection.&lt;br /&gt;
* Microsoft Entra multi-tenant apps not supported.&lt;br /&gt;
* No post-login IdP session validity check.&lt;br /&gt;
* Dynamic role map requires restart for new root folders.&lt;br /&gt;
* Installer UI supports up to 10 static role pairs; runtime supports unlimited.&lt;br /&gt;
&lt;br /&gt;
==== SAML Authentication Properties ====&lt;br /&gt;
&lt;br /&gt;
To use SAML 2.0 SSO, set the &#039;&#039;Authenticator&#039;&#039; property to&lt;br /&gt;
&#039;&#039;com.carfey.suite.security.saml.SAMLAuthenticator&#039;&#039; and add the properties below. All&lt;br /&gt;
properties are prefixed with &#039;&#039;com.carfey.suite.security.saml.SAMLAuthenticator.&#039;&#039; (shown&lt;br /&gt;
without prefix in the table for brevity). Note the &#039;&#039;.saml.&#039;&#039; package segment in both values.&lt;br /&gt;
&lt;br /&gt;
Obsidian acts as a SAML Service Provider and supports SP-initiated browser login only. See&lt;br /&gt;
[[Authenticator#SAMLAuthenticator_(SAML_2.0_SSO)|Authenticator — SAMLAuthenticator]] for how the&lt;br /&gt;
login and logout flows behave and for the list of &#039;&#039;/saml&#039;&#039; endpoints.&lt;br /&gt;
&lt;br /&gt;
===== Required properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;entityId&#039;&#039; || SP entity ID registered at the IdP (e.g. &#039;&#039;https://app.example.com/obsidian&#039;&#039;). Obsidian also requires this value to appear in the assertion&#039;s &#039;&#039;AudienceRestriction&#039;&#039; when the IdP sends one.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;assertionConsumerServiceUrl&#039;&#039; || Exact ACS URL where the IdP posts responses. This must be Obsidian&#039;s callback endpoint, e.g. &#039;&#039;https://app.example.com/obsidian/saml/callback&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;idpMetadataUrl&#039;&#039; &#039;&#039;&#039;or&#039;&#039;&#039; &#039;&#039;idpMetadataFile&#039;&#039; || Source of the IdP federation metadata: an HTTP(S) URL, or an absolute path to a local XML file. Set exactly one of the two.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups&#039;&#039; || Name of the assertion attribute carrying group memberships (e.g. &#039;&#039;groups&#039;&#039;). Required unless &#039;&#039;dynamicRole.prefix&#039;&#039; is set instead.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.group&#039;&#039; || IdP group value for this mapping. &#039;&#039;&amp;lt;idx&amp;gt;&#039;&#039; is an arbitrary stable label (numeric or descriptive). Not needed if all roles come from &#039;&#039;dynamicRole&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.roleName&#039;&#039; || Obsidian role for this mapping. The installer requires at least one &#039;&#039;Admin&#039;&#039; mapping, and you need one in practice so that somebody can administer Obsidian. Valid roles: &#039;&#039;&#039;Admin&#039;&#039;&#039;, &#039;&#039;&#039;Write&#039;&#039;&#039;, &#039;&#039;&#039;LimitedRead&#039;&#039;&#039;, &#039;&#039;&#039;Operator&#039;&#039;&#039;, &#039;&#039;&#039;Author&#039;&#039;&#039;, and folder-based roles (&#039;&#039;&amp;lt;folder&amp;gt;-(Write|Author|Operator)&#039;&#039;). The &#039;&#039;API&#039;&#039; role has no effect on a SAML install, because the REST API is unavailable.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Because &#039;&#039;signRequests&#039;&#039; defaults to enabled, a default configuration also requires the three&lt;br /&gt;
&#039;&#039;keystore&#039;&#039; properties below. Disable &#039;&#039;signRequests&#039;&#039; only if your IdP does not require signed&lt;br /&gt;
authentication requests.&lt;br /&gt;
&lt;br /&gt;
===== Optional properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Default !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;displayName&#039;&#039; || (none) || Label on the SSO button: &amp;quot;Sign in with &#039;&#039;&amp;lt;displayName&amp;gt;&#039;&#039;&amp;quot;. Defaults to &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;signRequests&#039;&#039; || &#039;&#039;true&#039;&#039; || Signs the outbound &#039;&#039;AuthnRequest&#039;&#039; and SP &#039;&#039;LogoutRequest&#039;&#039;. Requires the three &#039;&#039;keystore&#039;&#039; properties.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.path&#039;&#039; || (none) || Path to the keystore holding the SP signing key. Required when &#039;&#039;signRequests&#039;&#039; is enabled.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.password&#039;&#039; || (none) || Keystore password. Keep out of version control.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.alias&#039;&#039; || (none) || Alias of the SP signing key within the keystore.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;wantAssertionsSigned&#039;&#039; || &#039;&#039;true&#039;&#039; || Requires the IdP to sign assertions, verified against the signing certificate in the IdP metadata.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;singleLogoutServiceUrl&#039;&#039; || (none) || SP single logout URL. When set it is published in Obsidian&#039;s SP metadata and is matched against the &#039;&#039;Destination&#039;&#039; of an incoming &#039;&#039;LogoutRequest&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;logout.mode&#039;&#039; || &#039;&#039;local&#039;&#039; || &#039;&#039;local&#039;&#039;: invalidates the Obsidian session only. &#039;&#039;slo&#039;&#039;: SP-initiated single logout via the IdP&#039;s HTTP-Redirect &#039;&#039;SingleLogoutService&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;postLogoutRedirectUri&#039;&#039; || (none) || Where to redirect after the IdP&#039;s &#039;&#039;LogoutResponse&#039;&#039;. When blank the browser returns to &#039;&#039;/&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;nameIdFormat&#039;&#039; || &#039;&#039;urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified&#039;&#039; || &#039;&#039;NameIDPolicy&#039;&#039; format requested from the IdP. A transient format logs a startup warning, because transient NameIDs cannot serve as stable user IDs.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.user&#039;&#039; || &#039;&#039;nameId&#039;&#039; || Assertion attribute used as the internal user ID in audit logs. &#039;&#039;&#039;Strongly recommend keeping as &#039;&#039;nameId&#039;&#039;&#039;&#039;&#039;; any other value logs a startup warning.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.displayName&#039;&#039; || (none) || Attribute shown in the Obsidian UI nav bar. Separate from &#039;&#039;attribute.user&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.friendlyName&#039;&#039; || (none) || Match the groups attribute on its &#039;&#039;FriendlyName&#039;&#039; instead of its &#039;&#039;Name&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.valuePath&#039;&#039; || (none) || Sub-field name when the groups attribute holds structured values.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.stripPrefix&#039;&#039; || (none) || Prefix to strip from each group value (e.g. &#039;&#039;/&#039;&#039; for Keycloak full-path style).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;metadata.ttlHours&#039;&#039; || &#039;&#039;24&#039;&#039; || IdP federation metadata cache TTL in hours. A failed refresh falls back to the cached copy with a warning.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clockSkewSeconds&#039;&#039; || &#039;&#039;60&#039;&#039; || Clock skew tolerance for &#039;&#039;NotBefore&#039;&#039; / &#039;&#039;NotOnOrAfter&#039;&#039; validation (seconds).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;forceAuthn&#039;&#039; || &#039;&#039;false&#039;&#039; || Sets &#039;&#039;ForceAuthn=&amp;quot;true&amp;quot;&#039;&#039; on the &#039;&#039;AuthnRequest&#039;&#039;, asking the IdP to reauthenticate the user.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;authnContextClassRef&#039;&#039; || (none) || Requests a specific &#039;&#039;RequestedAuthnContext&#039;&#039;. A differing context in the response is logged as a warning, not rejected.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.prefix&#039;&#039; || (none) || Prefix for auto-generated folder-role group mappings. Usable on its own. The map is built at startup, so adding a new root job folder requires a restart.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.suffix&#039;&#039; || (none) || Optional suffix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.prefix&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;requireRole&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, browser SSO fails if the user&#039;s group attributes map to no Obsidian roles.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Security notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Encrypted assertions are not supported&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Obsidian requires signed, unencrypted assertions. If your SP metadata carries no signing key&lt;br /&gt;
material, Obsidian logs a startup warning: IdPs such as AD FS may then choose to encrypt the&lt;br /&gt;
assertion, which this release cannot process. Configure the SP keystore and register the&lt;br /&gt;
generated metadata so the IdP sees a signing &#039;&#039;KeyDescriptor&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Do not disable both signature settings&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Setting &#039;&#039;signRequests&#039;&#039; and &#039;&#039;wantAssertionsSigned&#039;&#039; both to &#039;&#039;false&#039;&#039; is accepted but logs a&lt;br /&gt;
startup warning. In that state assertions are neither signed nor verified, which is not safe for&lt;br /&gt;
production.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SP-initiated flows and replay&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Obsidian issues an &#039;&#039;AuthnRequest&#039;&#039; and tracks its ID in the browser session for five minutes.&lt;br /&gt;
When the IdP&#039;s response carries &#039;&#039;InResponseTo&#039;&#039;, it must match one of those pending IDs and is&lt;br /&gt;
consumed on use, so the same response cannot be replayed. There is no persistent store of&lt;br /&gt;
processed assertion IDs, so keep assertion lifetimes short at the IdP.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Clustered deployments and IdP-initiated logout&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
An IdP-initiated &#039;&#039;LogoutRequest&#039;&#039; can only end the Obsidian session on the node that receives&lt;br /&gt;
it. Use sticky sessions if you rely on single logout across a cluster.&lt;br /&gt;
&lt;br /&gt;
===== Keycloak example =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.saml.SAMLAuthenticator&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.entityId=https://app.example.com/obsidian&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.assertionConsumerServiceUrl=https://app.example.com/obsidian/saml/callback&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.idpMetadataUrl=https://keycloak.example.com/realms/obsidian/protocol/saml/descriptor&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.path=/opt/obsidian/saml-sp.p12&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.password=changeit&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.alias=obsidian-sp&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.attribute.groups=groups&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.1.group=Obsidian-Admins&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.1.roleName=Admin&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.2.group=Obsidian-Write&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.2.roleName=Write&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.3.group=Obsidian-Read&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.3.roleName=LimitedRead&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.displayName=Keycloak&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.logout.mode=local&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.requireRole=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Provider notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Microsoft Entra ID&#039;&#039;&#039;: group claims are omitted from SAML assertions once an account belongs to&lt;br /&gt;
roughly 150 groups. Filter the groups sent to the application, or map app roles instead. Note this&lt;br /&gt;
threshold differs from the OIDC one described above.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AD FS&#039;&#039;&#039;: the federation metadata document is normally at&lt;br /&gt;
&#039;&#039;/FederationMetadata/2007-06/FederationMetadata.xml&#039;&#039;. Register signing-capable SP metadata so AD FS&lt;br /&gt;
does not encrypt the assertion, which Obsidian cannot decrypt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Okta&#039;&#039;&#039;: use the application&#039;s SAML metadata URL for &#039;&#039;idpMetadataUrl&#039;&#039;. Okta&#039;s logout endpoints&lt;br /&gt;
may be POST-only; if no HTTP-Redirect &#039;&#039;SingleLogoutService&#039;&#039; is advertised, &#039;&#039;logout.mode=slo&#039;&#039;&lt;br /&gt;
falls back to local logout.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auth0&#039;&#039;&#039;: federation metadata is served at &#039;&#039;samlp/metadata/&amp;lt;client-id&amp;gt;&#039;&#039;. Group memberships must&lt;br /&gt;
be added to the assertion with an Auth0 Action or rule.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Keycloak and other generic IdPs&#039;&#039;&#039;: Keycloak publishes a SAML descriptor at&lt;br /&gt;
&#039;&#039;/realms/&amp;lt;realm&amp;gt;/protocol/saml/descriptor&#039;&#039;. Any IdP that publishes SAML 2.0 metadata with an&lt;br /&gt;
HTTP-POST &#039;&#039;AssertionConsumerService&#039;&#039; target and a signing certificate will work.&lt;br /&gt;
&lt;br /&gt;
===== v1 limitations =====&lt;br /&gt;
&lt;br /&gt;
* The REST API is unavailable while &#039;&#039;SAMLAuthenticator&#039;&#039; is active — there is no Bearer or JWT equivalent.&lt;br /&gt;
* SP-initiated login only.&lt;br /&gt;
* Encrypted assertions are not supported.&lt;br /&gt;
* No persistent assertion replay store beyond the single-use &#039;&#039;InResponseTo&#039;&#039; match and the assertion time window.&lt;br /&gt;
* Dynamic role map (&#039;&#039;dynamicRole.prefix&#039;&#039;/&#039;&#039;suffix&#039;&#039;) requires a server restart when new root job folders are added.&lt;br /&gt;
* Installer UI supports up to 10 static role pairs; the runtime supports unlimited.&lt;br /&gt;
&lt;br /&gt;
=== SMTP Mail Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Email configuration for notifications, if desired&lt;br /&gt;
#for straight up open relay, just specify the host using&lt;br /&gt;
mail.smtp.host=smtp.myopenrelayhost.com&lt;br /&gt;
mail.smtp.port=port &lt;br /&gt;
#(standard ports are 25, 465 for SSL, 587 for TLS)&lt;br /&gt;
&lt;br /&gt;
#for using TLS and SSL, provide these as necessary&lt;br /&gt;
mail.smtp.socketFactory.port=port&lt;br /&gt;
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory&lt;br /&gt;
mail.smtp.auth=true&lt;br /&gt;
mail.smtp.user=myauthenticatedemailuser@email.com&lt;br /&gt;
mail.smtp.password=PASSWORD&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 2.5.0, we support JNDI lookups for email sessions. All other email properties can be excluded.&lt;br /&gt;
mail.session.jndi.path=java:comp/env/mail/session&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Database Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Database configuration&lt;br /&gt;
com.carfey.obsidian.db.url=jdbc:mysql://localhost/obsidian&lt;br /&gt;
com.carfey.obsidian.db.userId=user&lt;br /&gt;
com.carfey.obsidian.db.password=pass&lt;br /&gt;
&lt;br /&gt;
# Sample JDBC URL formats for all platforms&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:mysql://localhost:3306/obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:h2:C:/dev/workspace/obsidian;MVCC=TRUE&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:oracle:thin:@localhost:1521:obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:postgresql://localhost:5432/obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:sqlserver://localhost:1433;databaseName=obsidian&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 6.3.0, we support constructing simple JDBC urls from component properties&lt;br /&gt;
# Only supports basic URL formats noted above and does not support H2&lt;br /&gt;
#com.carfey.obsidian.db.host=localhost&lt;br /&gt;
#com.carfey.obsidian.db.port=3306&lt;br /&gt;
#com.carfey.obsidian.db.databaseName=obsidianDB&lt;br /&gt;
#com.carfey.obsidian.db.dbType=mariadb/mysql/oracle/postgresql/sqlserver&lt;br /&gt;
#com.carfey.obsidian.db.oracleSid=SIDORCL&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 2.5.0, we support JNDI lookups for database connectivity.&lt;br /&gt;
com.carfey.obsidian.db.url=java:comp/env/jdbc/obsidian&lt;br /&gt;
com.carfey.obsidian.db.jndiType=mysql&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=mysql is also used for MariaDB&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=oracle&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=postgresql&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=h2&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=sqlserver&lt;br /&gt;
&lt;br /&gt;
# Table prefix may be used to add a prefix to tables and related database objects. It must be 6 or fewer characters and can contain letters or underscores. Available as of version 2.0.&lt;br /&gt;
com.carfey.obsidian.db.tablePrefix=OBSDN_&lt;br /&gt;
&lt;br /&gt;
# For Oracle databases, when Obsidian&#039;s tables exist in a different schema from the user specified above, or if the user does not default to the schema matching its name, specify the target schema here. Available as of version 2.1.&lt;br /&gt;
# For details on the required privileges to run with an alternate user, see https://wiki.obsidianscheduler.com/docs/Obsidian_Tables#Oracle_Privileges&lt;br /&gt;
# This same configuration item can be used to configure the Postgresql schema. If unspecified, the default is public. Available as of Obsidian 2.1.1. &lt;br /&gt;
# If using JNDI and Oracle/Postgresql, we recommend you set this value to ensure best performance.&lt;br /&gt;
com.carfey.obsidian.db.schema=obsidian&lt;br /&gt;
&lt;br /&gt;
#default max connections per pool if not specified is 50&lt;br /&gt;
com.carfey.obsidian.db.maxConnections=40&lt;br /&gt;
&lt;br /&gt;
#default millis timeout to retrieve available connection from pool if not specified is 2000&lt;br /&gt;
com.carfey.obsidian.db.connectionTimeout=2000&lt;br /&gt;
&lt;br /&gt;
# As of 3.4.0, you can disable pooling of connections, which is suggested only if using another connection pool through JNDI. When true, maxConnections is ignored. &lt;br /&gt;
# When a JNDI data source is selected in the installer, this is set to true by default.&lt;br /&gt;
com.carfey.obsidian.db.disablePooling=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.3.0, Oracle database supports configured wait time.&lt;br /&gt;
com.carfey.obsidian.db.timedLockWaitEnabled=true (default)&lt;br /&gt;
com.carfey.obsidian.db.timedLockWaitSeconds=1 (default, prior to 4.3.0, indefinite wait)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Logging Properties ===&lt;br /&gt;
As of &#039;&#039;&#039;Obsidian 5.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Please see [https://logging.apache.org/log4j/2.x/manual/configuration.html Log4j2 configuration]&lt;br /&gt;
&lt;br /&gt;
Before &#039;&#039;&#039;Obsidian 5.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Please see [https://logging.apache.org/log4j/1.2/manual.html Log4j Configuration]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Miscellaneous Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# If you wish to change the tokens used to reference global parameters in your job configuration, use the following two values:&lt;br /&gt;
global.param.start.token={{&lt;br /&gt;
global.param.end.token=}}&lt;br /&gt;
&lt;br /&gt;
# Populated by the installer, these values can be used to initialize licence key information into the database.&lt;br /&gt;
com.carfey.obsidian.licence.key=licenceKey&lt;br /&gt;
# Name is only required for hardware-linked or site licenses. Use the name exactly as provided by Carfey Software Corporation in your license email.&lt;br /&gt;
com.carfey.obsidian.licence.name=licenceeName&lt;br /&gt;
&lt;br /&gt;
# As of 2.8.0, if you have issues with classloading (e.g. in Grails or Spring), this flag enables usage of the context classloader .&lt;br /&gt;
com.carfey.jdk.useContextClassLoader=true&lt;br /&gt;
&lt;br /&gt;
# As of 2.9.0, you can set the scheduler host designator name multiple times in the same JVM by using this parameter:&lt;br /&gt;
com.carfey.obsidian.schedulerDesignation=obsidian-dev&lt;br /&gt;
&lt;br /&gt;
# As of 3.0.0, Job Forking can be enabled and configured per node&lt;br /&gt;
com.carfey.obsidian.jvmJobForkingEnabledOnThisNode=true&lt;br /&gt;
# This property is the location of the fork scripts. Obsidian is bundled with obsidianForkedJob.bat and obsidianForkedJob.sh. These are the expected script names.&lt;br /&gt;
com.carfey.obsidian.forkedJobScriptLocation=/Obsidian-3.0.0&lt;br /&gt;
# Use the following optional property if you need to override the default classpath that is built using the contents of the standalone directory.&lt;br /&gt;
# This allows for job forking support in embedded and even webapp deployments. Use the classpath format supported by your operating system.&lt;br /&gt;
#com.carfey.obsidian.forkedJobscriptClasspathOverride=&lt;br /&gt;
# Use the following optional property if you wish to include database configuration parameters as arguments passed to the forking scripts and the target forked Obsidian class.&lt;br /&gt;
# For example, you may need to do this if your configuration is done via System Property overrides or programmmatically in your embedded Obsidian instance.&lt;br /&gt;
#com.carfey.obsidian.jvmJobForkingIncludeDbSysParms=true&lt;br /&gt;
&lt;br /&gt;
# Obsolete as of 4.0.0. As of 3.4.0, the base URL used to resolve links in the Obsidian web app can be overridden. This is typically only required when a load balancer or proxy is used to access Obsidian.&lt;br /&gt;
com.carfey.obsidian.baseHrefUrl=http://myhost:8080/obsidian&lt;br /&gt;
&lt;br /&gt;
# As of 4.3.0, description/parameter formatting is supported. Custom formatter (com.carfey.ops.job.config.formatter.Formatter) supported via this parameter.&lt;br /&gt;
com.carfey.obsidian.formatterClass=com.carfey.ops.job.config.formatter.MarkdownFormatter (default)&lt;br /&gt;
&lt;br /&gt;
# As of 4.4.0, support for running DDL outside Obsidian and applying only the data portion of upgrades. May require incremental upgrades. Contact support for assistance. Enabled with the following:&lt;br /&gt;
com.carfey.obsidian.runner.skipDDL=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.5.0, Slack notifications can be automatically configured for FATAL, ERROR and WARN events by setting this property with a Slack incoming web hook URL. The user name property is optional and defaults to Obsidian Scheduler&lt;br /&gt;
com.carfey.obsidian.slack.webhookUrl=https://hooks.slack.com/services/XXXXXXX/YYYYYYYY/ZZZZZZZZZZ&lt;br /&gt;
com.carfey.obsidian.slack.userName=Obsidian Scheduler (prod)&lt;br /&gt;
&lt;br /&gt;
# As of 4.6.0, Obsidian supports starting a node in paused state. This can also be set as a System property.&lt;br /&gt;
com.carfey.obsidian.schedulerPausedOnStartup=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.10.0, Obsidian supports disabling script engine jobs selectively.&lt;br /&gt;
com.carfey.ops.job.script.BeanShellJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.GroovyJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.RubyJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.PythonJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.JavaScriptJob.enabled=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.0.0, Signal interrupt (supported as of 4.5.1) is disabled by default. Use configuration to enable.&lt;br /&gt;
com.carfey.obsidian.signalHandlerActiveForScheduler=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.0, you can send Obsidian events to a REST endpoint via an out-of-the-box Event Hook. The basicAuthorization property is optional, the sample showing its use for myusername:mypassword. The &amp;quot;Basic &amp;quot; prefix is added by Obsidian.&lt;br /&gt;
com.carfey.obsidian.restfulEventHook.url=https://myendpoint.com:1234/rest/obsidian_events&lt;br /&gt;
com.carfey.obsidian.restfulEventHook.basicAuthorization=bXl1c2VybmFtZTpteXBhc3N3b3Jk&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.1, maintenance jobs are automatically scheduled for new installations. Set the following property to disable this behaviour.&lt;br /&gt;
com.carfey.obsidian.skipMaintenanceJobInitialization=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.1, you can send Obsidian events to standard output and error streams using an out-of-the-box Event Hook. You can enable the default behaviour and auto-register it by adding the following configuration.&lt;br /&gt;
com.carfey.obsidian.standardOutputStreamsEventHook.enabled=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Table Prefixes ===&lt;br /&gt;
As shown in full configuration reference, a table name prefix may be specified to create Obsidian tables with names beginning with a specified string.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Table prefix may be used to add a prefix to tables and related database objects. It must be 6 or fewer characters and can contain letters or underscores. Available as of version 2.0.&lt;br /&gt;
com.carfey.obsidian.db.tablePrefix=OBSDN_&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Properties/Yaml File ==&lt;br /&gt;
&lt;br /&gt;
The above configuration must reside in a properties file named &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; or a yaml file named &amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; found according to the rules of [http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String) ClassLoader.getResource].  If both yaml and properties files are found, yaml is loaded first and properties values will override. Some possibilities include &amp;lt;code&amp;gt;WEB-INF/classes/com.carfey.(properties/yaml)&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;com.carfey.(properties/yaml)&amp;lt;/code&amp;gt; file in a directory which is explicitly added to the classpath, or at the root of a jar file as is done in &amp;lt;code&amp;gt;obsidian-props.jar&amp;lt;/code&amp;gt; for standalone deployments. Prior to Obsidian 3.6, this file had to exist on the classpath, even if only using the override options below to configure Obsidian.&lt;br /&gt;
&lt;br /&gt;
Instead of using a classpath resource for properties, you can specify an external properties file using the Java system property &amp;lt;code&amp;gt;carfey.properties.file&amp;lt;/code&amp;gt;. All expected properties must be specified in either the default &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; file on the classpath or in the override file. Any properties found in both files will use the override file&#039;s values. Usage: &amp;lt;code&amp;gt;-Dcarfey.properties.file=/home/obsidian/obsidian.properties&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Instead of using a classpath resource for yaml, you can specify an external yaml file using the Java system property &amp;lt;code&amp;gt;carfey.yaml.file&amp;lt;/code&amp;gt;. All expected configuration values must be specified in either the default &amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; file on the classpath or in the override file. Any configuration values found in both files will use the override file&#039;s values. Usage: &amp;lt;code&amp;gt;-Dcarfey.yaml.file=/home/obsidian/obsidian.yaml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 2.1, you may also use a programmatic properties override. Simply call &amp;lt;code&amp;gt;com.carfey.jdk.sys.Configurator.setOverride(Properties props)&amp;lt;/code&amp;gt; as early as possible in the application startup.  To quote the javadoc of this method, &#039;&#039;For use as a programmatic properties override. This must be called before any classes are accessed either through invocation or class initialization that may require access to configuration done through properties. Best if done as early as possible in code, perhaps first in an entry point class that does little else and then hands-off to existing entry point.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 4.4, you may specify configuration values as System Properties or Environment Variables. Duplicated values are resolved as follows:&lt;br /&gt;
# System Property&lt;br /&gt;
# Environment Variable - &#039;&#039;&#039;&#039;&#039;overridden by System Property&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
# Properties - &#039;&#039;&#039;&#039;&#039;overridden by Environment Variable and System Property&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Notifications Configuration ==&lt;br /&gt;
&lt;br /&gt;
Sending notifications requires SMTP configuration to be defined in the Obsidian properties file. The properties file reference at the beginning of this page includes SMTP configuration details.&lt;br /&gt;
&lt;br /&gt;
In addition to the properties file, you can configure some Notifications settings to tweak how your emails are sent. These values are configurable under the &#039;&#039;Dispatch&#039;&#039; category of the [[Admin Scheduler Settings|scheduler settings]] screen. Defaults are provided, but you can update them appropriately for your needs.  &lt;br /&gt;
&lt;br /&gt;
=== Notification sender address and DMARC ===&lt;br /&gt;
&lt;br /&gt;
The notificationFromEmail scheduler setting controls the From address used for notification emails. It is available under:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Scheduler Settings → Dispatch → notificationFromEmail&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Set this to an address belonging to your organization and authorized by the SMTP service configured for your Obsidian installation.&lt;br /&gt;
&lt;br /&gt;
Do not use ops-notification@obsidianscheduler.com when sending through your own SMTP infrastructure. Your SMTP server normally cannot authenticate mail for the obsidianscheduler.com domain. This can cause SPF, DKIM, and DMARC failures and may result in notifications being quarantined or rejected.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
scheduler-notifications@example.com&lt;br /&gt;
&lt;br /&gt;
After updating the value, trigger a test notification and confirm successful receipt.&lt;br /&gt;
&lt;br /&gt;
[[File:NotifSettings_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
== Dependent Libraries ==&lt;br /&gt;
&lt;br /&gt;
Obsidian Scheduler requires a number of third party libraries, both for the web administration application and the scheduler itself.  Below is information on these libraries and how they are used.  Unless otherwise noted, they are mandatory. As of Obsidian 2.5.0, our installer supports selective [[Installation_Guide#Configuring_3rd_Party_Library_Conflict_Management|conflict management]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Authentication (OAuth / OIDC)&#039;&#039;&lt;br /&gt;
All libraries in this section are only required when OAuthAuthenticator is active. &#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* oauth2-oidc-sdk-11.23.1.jar, nimbus-jose-jwt-10.0.2.jar. Nimbus OAuth 2.0 SDK with OpenID Connect extensions and JOSE+JWT support.&lt;br /&gt;
* content-type-2.3.jar, lang-tag-1.7.jar. Supporting libraries for the Nimbus SDK.&lt;br /&gt;
* json-smart-2.5.2.jar, accessors-smart-2.5.2.jar. JSON parsing for the Nimbus SDK.&lt;br /&gt;
* jcip-annotations-1.0-1.jar. Concurrency annotations for the Nimbus SDK.&lt;br /&gt;
* asm-9.7.1.jar. Bytecode manipulation library; transitive dependency of the Nimbus SDK.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Authentication (SAML 2.0)&#039;&#039;&lt;br /&gt;
All libraries in this section are only required when SAMLAuthenticator is active; the installer removes them when SAML is not the selected authentication type. &#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* opensaml-core-4.3.2.jar, opensaml-saml-api-4.3.2.jar, opensaml-saml-impl-4.3.2.jar, opensaml-security-api-4.3.2.jar, opensaml-security-impl-4.3.2.jar, opensaml-xmlsec-api-4.3.2.jar, opensaml-xmlsec-impl-4.3.2.jar, opensaml-messaging-api-4.3.2.jar, opensaml-profile-api-4.3.2.jar, opensaml-soap-api-4.3.2.jar, opensaml-soap-impl-4.3.2.jar, opensaml-storage-api-4.3.2.jar. OpenSAML 4 modules implementing SAML 2.0 messages, metadata and signature handling.&lt;br /&gt;
* java-support-8.4.2.jar. Shibboleth utility library required by OpenSAML.&lt;br /&gt;
* xmlsec-2.3.4.jar. Apache Santuario XML Signature implementation.&lt;br /&gt;
* cryptacular-1.2.5.jar, bcprov-jdk18on-1.85.jar, bcpkix-jdk18on-1.85.jar, bcutil-jdk18on-1.85.jar. Cryptographic providers used for assertion signature verification and SP key handling.&lt;br /&gt;
* velocity-engine-core-2.3.jar. Template engine used by OpenSAML&#039;s message encoders.&lt;br /&gt;
* commons-lang3-3.20.0.jar, commons-codec-1.15.jar. Apache Commons utilities required by OpenSAML.&lt;br /&gt;
* httpclient-4.5.14.jar, httpcore-4.4.16.jar. HTTP client used to retrieve IdP federation metadata.&lt;br /&gt;
* guava-32.1.3-jre.jar, failureaccess-1.0.1.jar, listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar. Guava collections and its companion placeholder artifacts, required by OpenSAML.&lt;br /&gt;
* metrics-core-4.2.25.jar. Dropwizard Metrics, required by OpenSAML.&lt;br /&gt;
* slf4j-api-2.0.12.jar, log4j-slf4j2-impl-2.26.0.jar. SLF4J API and its log4j2 binding, so OpenSAML&#039;s diagnostic logging is routed into Obsidian&#039;s log4j2 configuration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Embedded Tomcat (Obsidian Embedded Tomcat JAR packs only)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* tomcat-embed-core-10.1.39.jar, tomcat-embed-el-10.1.39.jar — Apache Tomcat embedded components (Apache License 2.0). Present in &#039;&#039;&#039;embed-tomcat-lib/&#039;&#039;&#039; when an Embedded Tomcat pack is selected.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Common&#039;&#039;&lt;br /&gt;
* obsidian.jar.  Core Obsidian lib.&lt;br /&gt;
* gson-2.10.1. Supports REST API and web administration.&lt;br /&gt;
* jmustache-1.15.jar. Provides email templating.&lt;br /&gt;
* log4j-api-2.26.0.jar, log4j-core-2.26.0.jar.  Obsidian logging uses log4j2.&lt;br /&gt;
* snakeyaml-2.3.jar. Support for YAML configuration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Databases&#039;&#039;&lt;br /&gt;
* h2-1.4.200.jar.  H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* mariadb-java-client-2.7.2.jar. MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* mssql-jdbc-9.2.1.jre11.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc11-21.1.0.0.jar. Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-42.2.19.jar.  PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Mail&#039;&#039; &lt;br /&gt;
* activation-1.1.jar, javax.mail-1.6.2.jar. Used for javamail (javax) email notifications.&lt;br /&gt;
* angus-activation-2.0.1.jar, angus-mail-2.0.2.jar. Optionally used with Jakarta EE email notifications.&lt;br /&gt;
* jakarta.activation-api-2.1.2.jar, jakarta.mail-api-2.1.2.jar. Used for Jakarta EE email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Scripts&#039;&#039;&lt;br /&gt;
* bsh-2.0b5.jar.  Support for our Bean Shell script execution. (http://www.beanshell.org).  Only required for BeanShellJob usage.&lt;br /&gt;
* jruby-9.2.17.0.jar. Support for Ruby script execution (https://www.jruby.org/). Only required for RubyJob usage.&lt;br /&gt;
* jython-standalone-2.7.2.jar. Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Scripts - Prior to Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Groovy scripting shipped as a single uber archive: apache-groovy-all-4.0.24.jar&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Scripts - As of Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Groovy scripting ships as 13 modular JARs: groovy-4.0.24.jar, groovy-astbuilder-4.0.24.jar, groovy-datetime-4.0.24.jar, groovy-dateutil-4.0.24.jar, groovy-json-4.0.24.jar, groovy-jsr223-4.0.24.jar, groovy-macro-4.0.24.jar, groovy-macro-library-4.0.24.jar, groovy-nio-4.0.24.jar, groovy-sql-4.0.24.jar, groovy-templates-4.0.24.jar, groovy-typecheckers-4.0.24.jar, groovy-xml-4.0.24.jar.&lt;br /&gt;
Plus four owned transitives (included when Groovy is enabled): asm-analysis-9.7.1.jar, asm-tree-9.7.1.jar, asm-util-9.7.1.jar, javaparser-core-3.26.2.jar&lt;br /&gt;
All 17 JARs above are excluded when Groovy scripting is disabled at install time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Web Admin&#039;&#039;&lt;br /&gt;
* dom4j-2.1.3.jar.  XML utilities.&lt;br /&gt;
* flexmark-0.62.2.jar, flexmark-util-0.62.2.jar. Markdown formatting for Description and Parameter job annotations.&lt;br /&gt;
* jxl-2.6.12.jar. Excel file format utilities. Only required in web administration.&lt;br /&gt;
* opencsv-5.4.jar. Support for CSV downloads in UI.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Web Admin - As of Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Markdown rendering for job and parameter descriptions ships as 12 modular JARs:&lt;br /&gt;
flexmark-0.62.2.jar, flexmark-util-ast-0.62.2.jar, flexmark-util-builder-0.62.2.jar, flexmark-util-collection-0.62.2.jar, flexmark-util-data-0.62.2.jar, flexmark-util-dependency-0.62.2.jar, flexmark-util-format-0.62.2.jar, flexmark-util-html-0.62.2.jar, flexmark-util-misc-0.62.2.jar, flexmark-util-options-0.62.2.jar, flexmark-util-sequence-0.62.2.jar, flexmark-util-visitor-0.62.2.jar&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;, the following changes were made:&lt;br /&gt;
* &#039;&#039;&#039;log4j upgraded&#039;&#039;&#039;: log4j-api and log4j-core updated from 2.17.1 to 2.26.0.&lt;br /&gt;
* &#039;&#039;&#039;Modular Groovy libraries&#039;&#039;&#039;: Obsidian no longer ships &#039;&#039;&#039;apache-groovy-all-4.0.24.jar&#039;&#039;&#039;. Groovy scripting now uses 13 modular JARs (see &#039;&#039;Scripts&#039;&#039; above). When Groovy is disabled at install time, all 13 modules are excluded.&lt;br /&gt;
* &#039;&#039;&#039;Modular Flexmark libraries&#039;&#039;&#039;: Obsidian no longer ships &#039;&#039;&#039;flexmark-util-0.62.2.jar&#039;&#039;&#039;. Markdown rendering for job and parameter descriptions now uses 12 modular Flexmark JARs (see &#039;&#039;Web Admin&#039;&#039; above). * &#039;&#039;&#039;OAuth / OIDC authentication libraries&#039;&#039;&#039; added (Nimbus OAuth 2.0 SDK and transitive dependencies). Required only when OAuthAuthenticator is configured. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties|Authentication (OAuth/OIDC)]] above.&lt;br /&gt;
* &#039;&#039;&#039;SAML 2.0 authentication libraries&#039;&#039;&#039; added (OpenSAML 4 and its transitive dependencies, plus the SLF4J-to-log4j2 binding). Required only when SAMLAuthenticator is configured. See [[Advanced_Configuration#SAML_Authentication_Properties|Authentication (SAML 2.0)]] above.&lt;br /&gt;
* Embedded Tomcat runtime added (tomcat-embed-core-10.1.39.jar, tomcat-embed-el-10.1.39.jar). Present only when an Embedded Tomcat pack is selected. Replaces the Jetty quick-start distribution which has been removed.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 7.0.0&#039;&#039;&#039;, the following libraries were in use.&lt;br /&gt;
* log4j-api-2.17.1.jar, log4j-core-2.17.1.jar.  Obsidian logging uses log4j2.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 6.0.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* groovy-all-3.0.7.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.4.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* gson-2.8.6.jar. Supports REST API and web administration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.2.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* jakarta.activation-1.2.1.jar. Used for email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.0.3&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* &#039;&#039;Obsidian 5.0.2&#039;&#039; log4j-api-2.16.0.jar, log4j-core-2.16.0.jar.  &lt;br /&gt;
* &#039;&#039;Obsidian 5.0.1&#039;&#039; log4j-api-2.15.0.jar, log4j-core-2.15.0.jar.  &lt;br /&gt;
* &#039;&#039;Obsidian 5.0.0&#039;&#039; log4j-api-2.14.1.jar, log4j-core-2.14.1.jar.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.0.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* activation-1.1.jar, javax.mail-1.5.5.jar.  Used for email notifications.&lt;br /&gt;
* dom4j-1.6.1.jar. XML utilities.&lt;br /&gt;
* gson-2.7.jar. Supports REST API and web administration.&lt;br /&gt;
* bsh-2.0b4.jar.  Support for our Bean Shell script execution. (http://www.beanshell.org).  Only required for BeanShellJob usage.&lt;br /&gt;
* groovy-all-2.4.14.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
* jython-standalone-2.7.0.jar. Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
* jruby-complete-9.2.7.0.jar. Support for Ruby script execution (https://www.jruby.org/). Only required for RubyJob usage.&lt;br /&gt;
* jmustache-1.12.jar. Provides email templating.&lt;br /&gt;
* h2-1.4.192.  H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* log4j-1.2.9.jar.  Obsidian logging uses log4j.&lt;br /&gt;
* mariadb-java-client-1.4.5.jar.  MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* jtds-1.3.1.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc7-12.1.0.1.jar.  Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-9.4.1208.jre7.jar. PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
* opencsv-3.8.jar. Support for CSV downloads in UI.&lt;br /&gt;
* flexmark-0.19.6.jar, flexmark-util-0.19.6.jar (as of 4.3.0). Markdown formatting for Description and Parameter job annotations.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 4.5.1&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* groovy-all-2.4.7.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 4.0.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* javax.mail-1.5.2.jar.  Used for email notifications.&lt;br /&gt;
* jstl.jar, standard.jar.  Web utilities. Only required in web administration.&lt;br /&gt;
* gson-2.2.2.jar. Supports REST API and web administration.&lt;br /&gt;
* groovy-all-2.1.8.jar.  Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
* jython-standalone-2.5.3.jar.  Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
* jmustache-1.8.jar. Provides email templating.&lt;br /&gt;
* h2-1.3.154. H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* mariadb-java-client-1.1.5.jar. MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* jtds-1.2.8.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc6-11.2.0.3.jar. Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-9.0-801.jdbc4.jar.  PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.9.1&#039;&#039;&#039;, the following redundant library was in included in the Obsidian packaging.&lt;br /&gt;
* smtp.jar.  Used for email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.2.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* groovy-all-1.7.6.jar&lt;br /&gt;
* jython.jar (version 2.5.2rc2)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.1.1&#039;&#039;&#039;, the following libraries were also included.&lt;br /&gt;
* carfey-date-1.2.jar or carfey-date-1.1.jar.  Date math/manipulation.&lt;br /&gt;
* jdk-gen.jar, jdk.jar, suite-gen.jar, suite.jar.  Core Obsidian libs.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.1.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* ojdbc14.jar Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 1.5&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* gson-1.5.jar. Supports REST API and web administration.&lt;br /&gt;
&lt;br /&gt;
=== Required Libraries for Embedded API ===&lt;br /&gt;
&lt;br /&gt;
To use the [[Embedded_API|Embedded API]], the following resources should be imported. If you have newer versions of the same JARs in your application, feel free to use the newer version. The JAR files below can generally be found in the &#039;&#039;standalone&#039;&#039; directory if your installation. The &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; file can be extracted from the &amp;lt;code&amp;gt;obsidian-properties-configuration.jar&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;obsidian-yaml-configuration.jar&amp;lt;/code&amp;gt; in the &#039;&#039;standalone&#039;&#039; directory, or from an Obsidian WAR file under &amp;lt;code&amp;gt;WEB-INF/classes&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* com.carfey.properties/com.carfey.yaml (configuration file), or obsidian-properties-configuration.jar/obsidian-yaml-configuration.jar, which contains the configuration file&lt;br /&gt;
* obsidian.jar&lt;br /&gt;
* dom4j-2.1.3.jar&lt;br /&gt;
* flexmark-0.62.2.jar plus the modular Flexmark JARs listed under &#039;&#039;Web Admin&#039;&#039; above&lt;br /&gt;
* gson-2.10.1&lt;br /&gt;
* log4j-api-2.26.0.jar, log4j-core-2.26.0.jar&lt;br /&gt;
* Appropriate JDBC JAR (e.g. mariadb-java-client-2.7.2.jar for MySQL or MariaDB)&lt;br /&gt;
&lt;br /&gt;
In addition, the following resources should be included if you wish to have notification support enabled when using the API:&lt;br /&gt;
* jmustache-1.15.jar&lt;br /&gt;
** jakarta.activation-api-2.1.2.jar, jakarta.mail-api-2.1.2.jar. Used for Jakarta EE email notifications.&lt;br /&gt;
** angus-activation-2.0.1.jar, angus-mail-2.0.2.jar. Optionally used with Jakarta EE email notifications.&lt;br /&gt;
** activation-1.1.jar, javax.mail-1.6.2.jar. Used for javamail (javax) email notifications.&lt;br /&gt;
&lt;br /&gt;
Finally, if you need to configure scripting jobs from the API, you will need to include any applicable JARs from the following list:&lt;br /&gt;
* apache-groovy-all-4.0.24.jar&lt;br /&gt;
* bsh-2.0b5.jar&lt;br /&gt;
* jruby-9.2.17.0.jar&lt;br /&gt;
* jython-standalone-2.7.2.jar&lt;br /&gt;
&lt;br /&gt;
== Disabling Job Execution &amp;amp; Scheduling in the Web Application ==&lt;br /&gt;
See the instructions within the [[Getting_Started#Disabling_Job_Scheduling_in_the_Web_Application|Getting Started Guide]].&lt;br /&gt;
&lt;br /&gt;
== Disabling Automatic Database Updates ==&lt;br /&gt;
See the instructions within the [[Getting_Started#Disabling_Automatic_Database_Updates|Getting Started Guide]].&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Advanced_Configuration&amp;diff=4131</id>
		<title>Advanced Configuration</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Advanced_Configuration&amp;diff=4131"/>
		<updated>2026-07-31T02:10:45Z</updated>

		<summary type="html">&lt;p&gt;Craig: /* Dependent Libraries */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian requires some initial configuration parameters for database connection information and desired authentication mechanisms.  The installer takes care of configuring these as provided.  For your reference should you wish to manually create/edit these, note the details below.&lt;br /&gt;
&lt;br /&gt;
== Configuration Details ==&lt;br /&gt;
&lt;br /&gt;
The following sections show the available properties and sample values that can be used in the &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; properties file. While properties format is shown, all the same configuration items apply to yaml support. See [[#Properties.2FYaml_File|Properties/Yaml File]] for more details on this file.&lt;br /&gt;
&lt;br /&gt;
=== Authentication Properties ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Comment out the native authenticator setup&lt;br /&gt;
#com.carfey.suite.security.Authenticator=com.carfey.suite.security.DBAuthenticator&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LDAP Authentication Properties ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#Set your LDAP info here&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.LdapAuthenticator&lt;br /&gt;
# As of Obsidian 2.1.1, you can specify the attribute type used in building up the distinguished name (dn). If unspecified, defaults to cn&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.dn.attribute=uid&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of dn.bases to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.dn.base=ou=people,o=MyOrgHere&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.url=ldap://localhost:10389&lt;br /&gt;
# As of Obsidian 2.1.1, you can specify the SECURITY_AUTHENTICATION. Defaults to simple. &lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.securityAuthentication=simple&lt;br /&gt;
# Any necessary additional information such as Provider, Principal and Host will need to be handled in your custom class extending LdapAuthenticator.&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 3.5.1, you can login via an LDAP attribute that is not part of the dn. A matching entry is found in the LDAP Directory (using anonymous or a fixed lookup account) &lt;br /&gt;
# after which the dn attribute above is retrieved to perform the user&#039;s authentication. The following 5 attributes are only used for this type of lookup and authentication.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.lookupDnAttribute=false&lt;br /&gt;
# As of Obsidian 3.5.1, this is the attribute name that is being searched for in the LDAP directory to build the dn for eventual authentication.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.loginAttribute=sAMAccountName&lt;br /&gt;
# As of Obsidian 3.5.1, this determines if these lookups will be done anonymously (without a session authenticated by user/password)&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.anonymousEnabled=false&lt;br /&gt;
# As of Obsidian 3.5.1, if anonymous lookups are not permitted or desired, provide the dn and password to be used for lookups. Typically, a read-only account with &lt;br /&gt;
# read rights to the LDAP entries and attributes in question is sufficient.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchUserFullDn=cn=ObsidianLDAPReadOnly,ou=people,o=MyOrgHere&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchPassword=password&lt;br /&gt;
# As of Obsidian 4.5.1, an alternate search base configuration parameter is used for the searchUser&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchUserDnBase=ou=people,o=MyOrgHere&lt;br /&gt;
# As of Obsidian 4.7.1, you may specify one or more additional group membership attributes beyond the defaults&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.groupMembershipAttribute=businessGroup~~~departmentName&lt;br /&gt;
&lt;br /&gt;
# Configure who may access the web app, based on LDAP group membership&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.accessDN=cn=SchedulerAccess,ou=groups,o=MyOrgHere&lt;br /&gt;
&lt;br /&gt;
# Configure the Write role used in the admin web app, based on LDAP group membership (by default, users may only read)&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.write.dn=cn=SchedulerWrite,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.write.roleName=Write&lt;br /&gt;
&lt;br /&gt;
# Configure the Admin role used in the admin web app, based on LDAP group membership (users may configure system parameters, etc).&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.admin.dn=cn=SchedulerAdmin,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.admin.roleName=Admin&lt;br /&gt;
&lt;br /&gt;
# Configure the LimitedRead role used in the admin web app, based on LDAP group membership (by default, users may only read)&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.limitedRead.dn=cn=SchedulerLimitedRead,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.limitedRead.roleName=LimitedRead&lt;br /&gt;
&lt;br /&gt;
# Configure the APIrole used by the REST API, based on LDAP group membership&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.api.dn=cn=SchedulerAPI,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.api.roleName=API&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OAuth / OIDC Authentication Properties ====&lt;br /&gt;
&lt;br /&gt;
To use OAuth / OIDC SSO, set the &#039;&#039;Authenticator&#039;&#039; property to&lt;br /&gt;
&#039;&#039;com.carfey.suite.security.OAuthAuthenticator&#039;&#039; and add the properties below. All&lt;br /&gt;
properties are prefixed with &#039;&#039;com.carfey.suite.security.OAuthAuthenticator.&#039;&#039; (shown&lt;br /&gt;
without prefix in the table for brevity).&lt;br /&gt;
&lt;br /&gt;
===== Required properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;issuer&#039;&#039; || OIDC Issuer URL. Obsidian fetches &#039;&#039;&amp;lt;issuer&amp;gt;/.well-known/openid-configuration&#039;&#039; lazily on first use and caches it.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clientId&#039;&#039; || Client ID registered at the IdP.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clientSecret&#039;&#039; || Client secret. Keep out of version control.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;redirectUri&#039;&#039; || Exact callback URL registered at the IdP (e.g. &#039;&#039;https://app.example.com/oauth/callback&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;scopes&#039;&#039; || Space-separated OAuth scopes (e.g. &#039;&#039;openid profile email groups&#039;&#039;). Must include &#039;&#039;openid&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groups&#039;&#039; || Name of the token claim that carries group memberships (e.g. &#039;&#039;groups&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.group&#039;&#039; || IdP group value for this mapping. &#039;&#039;&amp;lt;idx&amp;gt;&#039;&#039; is an arbitrary stable label (numeric or descriptive).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.roleName&#039;&#039; || Obsidian role for this mapping. At least one &#039;&#039;Admin&#039;&#039; mapping is required. Valid roles: &#039;&#039;&#039;Admin&#039;&#039;&#039;, &#039;&#039;&#039;API&#039;&#039;&#039;, &#039;&#039;&#039;Write&#039;&#039;&#039;, &#039;&#039;&#039;LimitedRead&#039;&#039;&#039;, &#039;&#039;&#039;Operator&#039;&#039;&#039;, &#039;&#039;&#039;Author&#039;&#039;&#039;, and folder-based roles (&#039;&#039;&amp;lt;folder&amp;gt;-(Write|Author|Operator)&#039;&#039;).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Optional properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Default !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;displayName&#039;&#039; || (none) || Label on the SSO button: &amp;quot;Sign in with &#039;&#039;&amp;lt;displayName&amp;gt;&#039;&#039;&amp;quot;. Defaults to &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;logout.mode&#039;&#039; || &#039;&#039;local&#039;&#039; || &#039;&#039;local&#039;&#039;: invalidates Obsidian session only. &#039;&#039;rp&#039;&#039;: RP-initiated logout via IdP &#039;&#039;end_session_endpoint&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;postLogoutRedirectUri&#039;&#039; || (none) || Where to redirect after RP-initiated logout. Ignored when &#039;&#039;logout.mode=local&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.user&#039;&#039; || &#039;&#039;sub&#039;&#039; || Claim used as the internal user ID in audit logs. &#039;&#039;&#039;Strongly recommend keeping as &#039;&#039;sub&#039;&#039;&#039;&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.displayName&#039;&#039; || &#039;&#039;preferred_username&#039;&#039; || Claim shown in the Obsidian UI nav bar. Separate from &#039;&#039;claim.user&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.validateTyp&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, rejects Bearer tokens where &#039;&#039;typ&#039;&#039; is present but not &#039;&#039;at+JWT&#039;&#039;. Set &#039;&#039;false&#039;&#039; only for legacy IdPs that omit &#039;&#039;typ&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clockSkewSeconds&#039;&#039; || &#039;&#039;60&#039;&#039; || Clock skew tolerance for &#039;&#039;exp&#039;&#039;/&#039;&#039;nbf&#039;&#039; validation (seconds).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;metadata.ttlHours&#039;&#039; || &#039;&#039;24&#039;&#039; || OIDC discovery document cache TTL in hours.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;expectedAccessTokenAudience&#039;&#039; || &#039;&#039;clientId&#039;&#039; || Expected &#039;&#039;aud&#039;&#039; for Bearer access tokens. See security note below.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsSource&#039;&#039; || &#039;&#039;idtoken&#039;&#039; || Where to read groups during browser SSO: &#039;&#039;idtoken&#039;&#039;, &#039;&#039;accesstoken&#039;&#039;, or &#039;&#039;userinfo&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsBearerSource&#039;&#039; || &#039;&#039;accesstoken&#039;&#039; || Where to read groups on the Bearer path: &#039;&#039;idtoken&#039;&#039; or &#039;&#039;accesstoken&#039;&#039;. &#039;&#039;&#039;userinfo is not permitted on the Bearer path.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsValuePath&#039;&#039; || (none) || Sub-field name when groups is an array of objects (e.g. &#039;&#039;name&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsStripPrefix&#039;&#039; || (none) || Prefix to strip from each group value (e.g. &#039;&#039;/&#039;&#039; for Keycloak full-path style).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsUserinfoTimeout&#039;&#039; || &#039;&#039;5&#039;&#039; || HTTP timeout in seconds for the userinfo endpoint. Only used when &#039;&#039;claim.groupsSource=userinfo&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.prefix&#039;&#039; || (none) || Prefix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.suffix&#039;&#039;. Map is rebuilt at startup; adding a new root folder requires a restart.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.suffix&#039;&#039; || (none) || Suffix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.prefix&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;requireRole&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, browser SSO fails if IdP groups map to no Obsidian roles. Does not apply to REST Bearer tokens.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Security notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;expectedAccessTokenAudience and cross-JWT confusion&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When &#039;&#039;expectedAccessTokenAudience&#039;&#039; is not set it defaults to &#039;&#039;clientId&#039;&#039;. In many OIDC&lt;br /&gt;
configurations the id_token also carries &#039;&#039;aud=clientId&#039;&#039;, which could allow an id_token&lt;br /&gt;
to be submitted as a Bearer token. The &#039;&#039;claim.validateTyp=true&#039;&#039; default mitigates this&lt;br /&gt;
for compliant IdPs (access tokens carry &#039;&#039;typ=at+JWT&#039;&#039;; id_tokens carry &#039;&#039;typ=JWT&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
For maximum defence, set &#039;&#039;expectedAccessTokenAudience&#039;&#039; to a distinct resource-server&lt;br /&gt;
identifier if your IdP supports it.&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;claim.validateTyp=false&#039;&#039; AND &#039;&#039;expectedAccessTokenAudience&#039;&#039; is not explicitly set,&lt;br /&gt;
Obsidian logs a mandatory startup WARN advising you to set &#039;&#039;expectedAccessTokenAudience&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
===== Keycloak example =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.OAuthAuthenticator&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.issuer=http://localhost:8081/realms/obsidian&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.clientId=obsidian-client&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.clientSecret=test-client-secret-local&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.redirectUri=http://localhost:8080/oauth/callback&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.scopes=openid profile email groups&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.claim.groups=groups&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.claim.groupsSource=idtoken&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.1.group=Obsidian-Admins&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.1.roleName=Admin&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.2.group=Obsidian-Write&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.2.roleName=Write&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.3.group=Obsidian-Read&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.3.roleName=LimitedRead&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.4.group=Obsidian-API&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.4.roleName=API&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.displayName=Keycloak&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.logout.mode=local&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.requireRole=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Provider notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Microsoft Entra ID&#039;&#039;&#039;: groups claim contains GUIDs by default; configure Optional&lt;br /&gt;
Claims for display names. Accounts with 200+ groups trigger overage. Multi-tenant apps not&lt;br /&gt;
supported in v1.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auth0&#039;&#039;&#039;: opaque access tokens (Auth0 default) are not supported on the Bearer path.&lt;br /&gt;
Configure a custom API audience to get JWT access tokens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Okta&#039;&#039;&#039;: use the Custom Authorization Server URL, not the Org Authorization Server.&lt;br /&gt;
&lt;br /&gt;
===== v1 limitations =====&lt;br /&gt;
&lt;br /&gt;
* Opaque access tokens not supported on the REST/Bearer path.&lt;br /&gt;
* No &#039;&#039;jti&#039;&#039; replay protection.&lt;br /&gt;
* Microsoft Entra multi-tenant apps not supported.&lt;br /&gt;
* No post-login IdP session validity check.&lt;br /&gt;
* Dynamic role map requires restart for new root folders.&lt;br /&gt;
* Installer UI supports up to 10 static role pairs; runtime supports unlimited.&lt;br /&gt;
&lt;br /&gt;
==== SAML Authentication Properties ====&lt;br /&gt;
&lt;br /&gt;
To use SAML 2.0 SSO, set the &#039;&#039;Authenticator&#039;&#039; property to&lt;br /&gt;
&#039;&#039;com.carfey.suite.security.saml.SAMLAuthenticator&#039;&#039; and add the properties below. All&lt;br /&gt;
properties are prefixed with &#039;&#039;com.carfey.suite.security.saml.SAMLAuthenticator.&#039;&#039; (shown&lt;br /&gt;
without prefix in the table for brevity). Note the &#039;&#039;.saml.&#039;&#039; package segment in both values.&lt;br /&gt;
&lt;br /&gt;
Obsidian acts as a SAML Service Provider and supports SP-initiated browser login only. See&lt;br /&gt;
[[Authenticator#SAMLAuthenticator_(SAML_2.0_SSO)|Authenticator — SAMLAuthenticator]] for how the&lt;br /&gt;
login and logout flows behave and for the list of &#039;&#039;/saml&#039;&#039; endpoints.&lt;br /&gt;
&lt;br /&gt;
===== Required properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;entityId&#039;&#039; || SP entity ID registered at the IdP (e.g. &#039;&#039;https://app.example.com/obsidian&#039;&#039;). Obsidian also requires this value to appear in the assertion&#039;s &#039;&#039;AudienceRestriction&#039;&#039; when the IdP sends one.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;assertionConsumerServiceUrl&#039;&#039; || Exact ACS URL where the IdP posts responses. This must be Obsidian&#039;s callback endpoint, e.g. &#039;&#039;https://app.example.com/obsidian/saml/callback&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;idpMetadataUrl&#039;&#039; &#039;&#039;&#039;or&#039;&#039;&#039; &#039;&#039;idpMetadataFile&#039;&#039; || Source of the IdP federation metadata: an HTTP(S) URL, or an absolute path to a local XML file. Set exactly one of the two.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups&#039;&#039; || Name of the assertion attribute carrying group memberships (e.g. &#039;&#039;groups&#039;&#039;). Required unless &#039;&#039;dynamicRole.prefix&#039;&#039; is set instead.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.group&#039;&#039; || IdP group value for this mapping. &#039;&#039;&amp;lt;idx&amp;gt;&#039;&#039; is an arbitrary stable label (numeric or descriptive). Not needed if all roles come from &#039;&#039;dynamicRole&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.roleName&#039;&#039; || Obsidian role for this mapping. The installer requires at least one &#039;&#039;Admin&#039;&#039; mapping, and you need one in practice so that somebody can administer Obsidian. Valid roles: &#039;&#039;&#039;Admin&#039;&#039;&#039;, &#039;&#039;&#039;Write&#039;&#039;&#039;, &#039;&#039;&#039;LimitedRead&#039;&#039;&#039;, &#039;&#039;&#039;Operator&#039;&#039;&#039;, &#039;&#039;&#039;Author&#039;&#039;&#039;, and folder-based roles (&#039;&#039;&amp;lt;folder&amp;gt;-(Write|Author|Operator)&#039;&#039;). The &#039;&#039;API&#039;&#039; role has no effect on a SAML install, because the REST API is unavailable.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Because &#039;&#039;signRequests&#039;&#039; defaults to enabled, a default configuration also requires the three&lt;br /&gt;
&#039;&#039;keystore&#039;&#039; properties below. Disable &#039;&#039;signRequests&#039;&#039; only if your IdP does not require signed&lt;br /&gt;
authentication requests.&lt;br /&gt;
&lt;br /&gt;
===== Optional properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Default !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;displayName&#039;&#039; || (none) || Label on the SSO button: &amp;quot;Sign in with &#039;&#039;&amp;lt;displayName&amp;gt;&#039;&#039;&amp;quot;. Defaults to &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;signRequests&#039;&#039; || &#039;&#039;true&#039;&#039; || Signs the outbound &#039;&#039;AuthnRequest&#039;&#039; and SP &#039;&#039;LogoutRequest&#039;&#039;. Requires the three &#039;&#039;keystore&#039;&#039; properties.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.path&#039;&#039; || (none) || Path to the keystore holding the SP signing key. Required when &#039;&#039;signRequests&#039;&#039; is enabled.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.password&#039;&#039; || (none) || Keystore password. Keep out of version control.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.alias&#039;&#039; || (none) || Alias of the SP signing key within the keystore.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;wantAssertionsSigned&#039;&#039; || &#039;&#039;true&#039;&#039; || Requires the IdP to sign assertions, verified against the signing certificate in the IdP metadata.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;singleLogoutServiceUrl&#039;&#039; || (none) || SP single logout URL. When set it is published in Obsidian&#039;s SP metadata and is matched against the &#039;&#039;Destination&#039;&#039; of an incoming &#039;&#039;LogoutRequest&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;logout.mode&#039;&#039; || &#039;&#039;local&#039;&#039; || &#039;&#039;local&#039;&#039;: invalidates the Obsidian session only. &#039;&#039;slo&#039;&#039;: SP-initiated single logout via the IdP&#039;s HTTP-Redirect &#039;&#039;SingleLogoutService&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;postLogoutRedirectUri&#039;&#039; || (none) || Where to redirect after the IdP&#039;s &#039;&#039;LogoutResponse&#039;&#039;. When blank the browser returns to &#039;&#039;/&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;nameIdFormat&#039;&#039; || &#039;&#039;urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified&#039;&#039; || &#039;&#039;NameIDPolicy&#039;&#039; format requested from the IdP. A transient format logs a startup warning, because transient NameIDs cannot serve as stable user IDs.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.user&#039;&#039; || &#039;&#039;nameId&#039;&#039; || Assertion attribute used as the internal user ID in audit logs. &#039;&#039;&#039;Strongly recommend keeping as &#039;&#039;nameId&#039;&#039;&#039;&#039;&#039;; any other value logs a startup warning.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.displayName&#039;&#039; || (none) || Attribute shown in the Obsidian UI nav bar. Separate from &#039;&#039;attribute.user&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.friendlyName&#039;&#039; || (none) || Match the groups attribute on its &#039;&#039;FriendlyName&#039;&#039; instead of its &#039;&#039;Name&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.valuePath&#039;&#039; || (none) || Sub-field name when the groups attribute holds structured values.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.stripPrefix&#039;&#039; || (none) || Prefix to strip from each group value (e.g. &#039;&#039;/&#039;&#039; for Keycloak full-path style).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;metadata.ttlHours&#039;&#039; || &#039;&#039;24&#039;&#039; || IdP federation metadata cache TTL in hours. A failed refresh falls back to the cached copy with a warning.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clockSkewSeconds&#039;&#039; || &#039;&#039;60&#039;&#039; || Clock skew tolerance for &#039;&#039;NotBefore&#039;&#039; / &#039;&#039;NotOnOrAfter&#039;&#039; validation (seconds).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;forceAuthn&#039;&#039; || &#039;&#039;false&#039;&#039; || Sets &#039;&#039;ForceAuthn=&amp;quot;true&amp;quot;&#039;&#039; on the &#039;&#039;AuthnRequest&#039;&#039;, asking the IdP to reauthenticate the user.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;authnContextClassRef&#039;&#039; || (none) || Requests a specific &#039;&#039;RequestedAuthnContext&#039;&#039;. A differing context in the response is logged as a warning, not rejected.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.prefix&#039;&#039; || (none) || Prefix for auto-generated folder-role group mappings. Usable on its own. The map is built at startup, so adding a new root job folder requires a restart.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.suffix&#039;&#039; || (none) || Optional suffix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.prefix&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;requireRole&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, browser SSO fails if the user&#039;s group attributes map to no Obsidian roles.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Security notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Encrypted assertions are not supported&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Obsidian requires signed, unencrypted assertions. If your SP metadata carries no signing key&lt;br /&gt;
material, Obsidian logs a startup warning: IdPs such as AD FS may then choose to encrypt the&lt;br /&gt;
assertion, which this release cannot process. Configure the SP keystore and register the&lt;br /&gt;
generated metadata so the IdP sees a signing &#039;&#039;KeyDescriptor&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Do not disable both signature settings&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Setting &#039;&#039;signRequests&#039;&#039; and &#039;&#039;wantAssertionsSigned&#039;&#039; both to &#039;&#039;false&#039;&#039; is accepted but logs a&lt;br /&gt;
startup warning. In that state assertions are neither signed nor verified, which is not safe for&lt;br /&gt;
production.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SP-initiated flows and replay&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Obsidian issues an &#039;&#039;AuthnRequest&#039;&#039; and tracks its ID in the browser session for five minutes.&lt;br /&gt;
When the IdP&#039;s response carries &#039;&#039;InResponseTo&#039;&#039;, it must match one of those pending IDs and is&lt;br /&gt;
consumed on use, so the same response cannot be replayed. There is no persistent store of&lt;br /&gt;
processed assertion IDs, so keep assertion lifetimes short at the IdP.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Clustered deployments and IdP-initiated logout&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
An IdP-initiated &#039;&#039;LogoutRequest&#039;&#039; can only end the Obsidian session on the node that receives&lt;br /&gt;
it. Use sticky sessions if you rely on single logout across a cluster.&lt;br /&gt;
&lt;br /&gt;
===== Keycloak example =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.saml.SAMLAuthenticator&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.entityId=https://app.example.com/obsidian&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.assertionConsumerServiceUrl=https://app.example.com/obsidian/saml/callback&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.idpMetadataUrl=https://keycloak.example.com/realms/obsidian/protocol/saml/descriptor&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.path=/opt/obsidian/saml-sp.p12&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.password=changeit&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.alias=obsidian-sp&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.attribute.groups=groups&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.1.group=Obsidian-Admins&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.1.roleName=Admin&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.2.group=Obsidian-Write&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.2.roleName=Write&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.3.group=Obsidian-Read&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.3.roleName=LimitedRead&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.displayName=Keycloak&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.logout.mode=local&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.requireRole=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Provider notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Microsoft Entra ID&#039;&#039;&#039;: group claims are omitted from SAML assertions once an account belongs to&lt;br /&gt;
roughly 150 groups. Filter the groups sent to the application, or map app roles instead. Note this&lt;br /&gt;
threshold differs from the OIDC one described above.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AD FS&#039;&#039;&#039;: the federation metadata document is normally at&lt;br /&gt;
&#039;&#039;/FederationMetadata/2007-06/FederationMetadata.xml&#039;&#039;. Register signing-capable SP metadata so AD FS&lt;br /&gt;
does not encrypt the assertion, which Obsidian cannot decrypt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Okta&#039;&#039;&#039;: use the application&#039;s SAML metadata URL for &#039;&#039;idpMetadataUrl&#039;&#039;. Okta&#039;s logout endpoints&lt;br /&gt;
may be POST-only; if no HTTP-Redirect &#039;&#039;SingleLogoutService&#039;&#039; is advertised, &#039;&#039;logout.mode=slo&#039;&#039;&lt;br /&gt;
falls back to local logout.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auth0&#039;&#039;&#039;: federation metadata is served at &#039;&#039;samlp/metadata/&amp;lt;client-id&amp;gt;&#039;&#039;. Group memberships must&lt;br /&gt;
be added to the assertion with an Auth0 Action or rule.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Keycloak and other generic IdPs&#039;&#039;&#039;: Keycloak publishes a SAML descriptor at&lt;br /&gt;
&#039;&#039;/realms/&amp;lt;realm&amp;gt;/protocol/saml/descriptor&#039;&#039;. Any IdP that publishes SAML 2.0 metadata with an&lt;br /&gt;
HTTP-POST &#039;&#039;AssertionConsumerService&#039;&#039; target and a signing certificate will work.&lt;br /&gt;
&lt;br /&gt;
===== v1 limitations =====&lt;br /&gt;
&lt;br /&gt;
* The REST API is unavailable while &#039;&#039;SAMLAuthenticator&#039;&#039; is active — there is no Bearer or JWT equivalent.&lt;br /&gt;
* SP-initiated login only.&lt;br /&gt;
* Encrypted assertions are not supported.&lt;br /&gt;
* No persistent assertion replay store beyond the single-use &#039;&#039;InResponseTo&#039;&#039; match and the assertion time window.&lt;br /&gt;
* Dynamic role map (&#039;&#039;dynamicRole.prefix&#039;&#039;/&#039;&#039;suffix&#039;&#039;) requires a server restart when new root job folders are added.&lt;br /&gt;
* Installer UI supports up to 10 static role pairs; the runtime supports unlimited.&lt;br /&gt;
&lt;br /&gt;
=== SMTP Mail Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Email configuration for notifications, if desired&lt;br /&gt;
#for straight up open relay, just specify the host using&lt;br /&gt;
mail.smtp.host=smtp.myopenrelayhost.com&lt;br /&gt;
mail.smtp.port=port &lt;br /&gt;
#(standard ports are 25, 465 for SSL, 587 for TLS)&lt;br /&gt;
&lt;br /&gt;
#for using TLS and SSL, provide these as necessary&lt;br /&gt;
mail.smtp.socketFactory.port=port&lt;br /&gt;
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory&lt;br /&gt;
mail.smtp.auth=true&lt;br /&gt;
mail.smtp.user=myauthenticatedemailuser@email.com&lt;br /&gt;
mail.smtp.password=PASSWORD&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 2.5.0, we support JNDI lookups for email sessions. All other email properties can be excluded.&lt;br /&gt;
mail.session.jndi.path=java:comp/env/mail/session&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Database Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Database configuration&lt;br /&gt;
com.carfey.obsidian.db.url=jdbc:mysql://localhost/obsidian&lt;br /&gt;
com.carfey.obsidian.db.userId=user&lt;br /&gt;
com.carfey.obsidian.db.password=pass&lt;br /&gt;
&lt;br /&gt;
# Sample JDBC URL formats for all platforms&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:mysql://localhost:3306/obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:h2:C:/dev/workspace/obsidian;MVCC=TRUE&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:oracle:thin:@localhost:1521:obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:postgresql://localhost:5432/obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:sqlserver://localhost:1433;databaseName=obsidian&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 6.3.0, we support constructing simple JDBC urls from component properties&lt;br /&gt;
# Only supports basic URL formats noted above and does not support H2&lt;br /&gt;
#com.carfey.obsidian.db.host=localhost&lt;br /&gt;
#com.carfey.obsidian.db.port=3306&lt;br /&gt;
#com.carfey.obsidian.db.databaseName=obsidianDB&lt;br /&gt;
#com.carfey.obsidian.db.dbType=mariadb/mysql/oracle/postgresql/sqlserver&lt;br /&gt;
#com.carfey.obsidian.db.oracleSid=SIDORCL&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 2.5.0, we support JNDI lookups for database connectivity.&lt;br /&gt;
com.carfey.obsidian.db.url=java:comp/env/jdbc/obsidian&lt;br /&gt;
com.carfey.obsidian.db.jndiType=mysql&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=mysql is also used for MariaDB&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=oracle&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=postgresql&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=h2&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=sqlserver&lt;br /&gt;
&lt;br /&gt;
# Table prefix may be used to add a prefix to tables and related database objects. It must be 6 or fewer characters and can contain letters or underscores. Available as of version 2.0.&lt;br /&gt;
com.carfey.obsidian.db.tablePrefix=OBSDN_&lt;br /&gt;
&lt;br /&gt;
# For Oracle databases, when Obsidian&#039;s tables exist in a different schema from the user specified above, or if the user does not default to the schema matching its name, specify the target schema here. Available as of version 2.1.&lt;br /&gt;
# For details on the required privileges to run with an alternate user, see https://wiki.obsidianscheduler.com/docs/Obsidian_Tables#Oracle_Privileges&lt;br /&gt;
# This same configuration item can be used to configure the Postgresql schema. If unspecified, the default is public. Available as of Obsidian 2.1.1. &lt;br /&gt;
# If using JNDI and Oracle/Postgresql, we recommend you set this value to ensure best performance.&lt;br /&gt;
com.carfey.obsidian.db.schema=obsidian&lt;br /&gt;
&lt;br /&gt;
#default max connections per pool if not specified is 50&lt;br /&gt;
com.carfey.obsidian.db.maxConnections=40&lt;br /&gt;
&lt;br /&gt;
#default millis timeout to retrieve available connection from pool if not specified is 2000&lt;br /&gt;
com.carfey.obsidian.db.connectionTimeout=2000&lt;br /&gt;
&lt;br /&gt;
# As of 3.4.0, you can disable pooling of connections, which is suggested only if using another connection pool through JNDI. When true, maxConnections is ignored. &lt;br /&gt;
# When a JNDI data source is selected in the installer, this is set to true by default.&lt;br /&gt;
com.carfey.obsidian.db.disablePooling=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.3.0, Oracle database supports configured wait time.&lt;br /&gt;
com.carfey.obsidian.db.timedLockWaitEnabled=true (default)&lt;br /&gt;
com.carfey.obsidian.db.timedLockWaitSeconds=1 (default, prior to 4.3.0, indefinite wait)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Logging Properties ===&lt;br /&gt;
As of &#039;&#039;&#039;Obsidian 5.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Please see [https://logging.apache.org/log4j/2.x/manual/configuration.html Log4j2 configuration]&lt;br /&gt;
&lt;br /&gt;
Before &#039;&#039;&#039;Obsidian 5.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Please see [https://logging.apache.org/log4j/1.2/manual.html Log4j Configuration]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Miscellaneous Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# If you wish to change the tokens used to reference global parameters in your job configuration, use the following two values:&lt;br /&gt;
global.param.start.token={{&lt;br /&gt;
global.param.end.token=}}&lt;br /&gt;
&lt;br /&gt;
# Populated by the installer, these values can be used to initialize licence key information into the database.&lt;br /&gt;
com.carfey.obsidian.licence.key=licenceKey&lt;br /&gt;
# Name is only required for hardware-linked or site licenses. Use the name exactly as provided by Carfey Software Corporation in your license email.&lt;br /&gt;
com.carfey.obsidian.licence.name=licenceeName&lt;br /&gt;
&lt;br /&gt;
# As of 2.8.0, if you have issues with classloading (e.g. in Grails or Spring), this flag enables usage of the context classloader .&lt;br /&gt;
com.carfey.jdk.useContextClassLoader=true&lt;br /&gt;
&lt;br /&gt;
# As of 2.9.0, you can set the scheduler host designator name multiple times in the same JVM by using this parameter:&lt;br /&gt;
com.carfey.obsidian.schedulerDesignation=obsidian-dev&lt;br /&gt;
&lt;br /&gt;
# As of 3.0.0, Job Forking can be enabled and configured per node&lt;br /&gt;
com.carfey.obsidian.jvmJobForkingEnabledOnThisNode=true&lt;br /&gt;
# This property is the location of the fork scripts. Obsidian is bundled with obsidianForkedJob.bat and obsidianForkedJob.sh. These are the expected script names.&lt;br /&gt;
com.carfey.obsidian.forkedJobScriptLocation=/Obsidian-3.0.0&lt;br /&gt;
# Use the following optional property if you need to override the default classpath that is built using the contents of the standalone directory.&lt;br /&gt;
# This allows for job forking support in embedded and even webapp deployments. Use the classpath format supported by your operating system.&lt;br /&gt;
#com.carfey.obsidian.forkedJobscriptClasspathOverride=&lt;br /&gt;
# Use the following optional property if you wish to include database configuration parameters as arguments passed to the forking scripts and the target forked Obsidian class.&lt;br /&gt;
# For example, you may need to do this if your configuration is done via System Property overrides or programmmatically in your embedded Obsidian instance.&lt;br /&gt;
#com.carfey.obsidian.jvmJobForkingIncludeDbSysParms=true&lt;br /&gt;
&lt;br /&gt;
# Obsolete as of 4.0.0. As of 3.4.0, the base URL used to resolve links in the Obsidian web app can be overridden. This is typically only required when a load balancer or proxy is used to access Obsidian.&lt;br /&gt;
com.carfey.obsidian.baseHrefUrl=http://myhost:8080/obsidian&lt;br /&gt;
&lt;br /&gt;
# As of 4.3.0, description/parameter formatting is supported. Custom formatter (com.carfey.ops.job.config.formatter.Formatter) supported via this parameter.&lt;br /&gt;
com.carfey.obsidian.formatterClass=com.carfey.ops.job.config.formatter.MarkdownFormatter (default)&lt;br /&gt;
&lt;br /&gt;
# As of 4.4.0, support for running DDL outside Obsidian and applying only the data portion of upgrades. May require incremental upgrades. Contact support for assistance. Enabled with the following:&lt;br /&gt;
com.carfey.obsidian.runner.skipDDL=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.5.0, Slack notifications can be automatically configured for FATAL, ERROR and WARN events by setting this property with a Slack incoming web hook URL. The user name property is optional and defaults to Obsidian Scheduler&lt;br /&gt;
com.carfey.obsidian.slack.webhookUrl=https://hooks.slack.com/services/XXXXXXX/YYYYYYYY/ZZZZZZZZZZ&lt;br /&gt;
com.carfey.obsidian.slack.userName=Obsidian Scheduler (prod)&lt;br /&gt;
&lt;br /&gt;
# As of 4.6.0, Obsidian supports starting a node in paused state. This can also be set as a System property.&lt;br /&gt;
com.carfey.obsidian.schedulerPausedOnStartup=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.10.0, Obsidian supports disabling script engine jobs selectively.&lt;br /&gt;
com.carfey.ops.job.script.BeanShellJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.GroovyJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.RubyJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.PythonJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.JavaScriptJob.enabled=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.0.0, Signal interrupt (supported as of 4.5.1) is disabled by default. Use configuration to enable.&lt;br /&gt;
com.carfey.obsidian.signalHandlerActiveForScheduler=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.0, you can send Obsidian events to a REST endpoint via an out-of-the-box Event Hook. The basicAuthorization property is optional, the sample showing its use for myusername:mypassword. The &amp;quot;Basic &amp;quot; prefix is added by Obsidian.&lt;br /&gt;
com.carfey.obsidian.restfulEventHook.url=https://myendpoint.com:1234/rest/obsidian_events&lt;br /&gt;
com.carfey.obsidian.restfulEventHook.basicAuthorization=bXl1c2VybmFtZTpteXBhc3N3b3Jk&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.1, maintenance jobs are automatically scheduled for new installations. Set the following property to disable this behaviour.&lt;br /&gt;
com.carfey.obsidian.skipMaintenanceJobInitialization=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.1, you can send Obsidian events to standard output and error streams using an out-of-the-box Event Hook. You can enable the default behaviour and auto-register it by adding the following configuration.&lt;br /&gt;
com.carfey.obsidian.standardOutputStreamsEventHook.enabled=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Table Prefixes ===&lt;br /&gt;
As shown in full configuration reference, a table name prefix may be specified to create Obsidian tables with names beginning with a specified string.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Table prefix may be used to add a prefix to tables and related database objects. It must be 6 or fewer characters and can contain letters or underscores. Available as of version 2.0.&lt;br /&gt;
com.carfey.obsidian.db.tablePrefix=OBSDN_&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Properties/Yaml File ==&lt;br /&gt;
&lt;br /&gt;
The above configuration must reside in a properties file named &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; or a yaml file named &amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; found according to the rules of [http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String) ClassLoader.getResource].  If both yaml and properties files are found, yaml is loaded first and properties values will override. Some possibilities include &amp;lt;code&amp;gt;WEB-INF/classes/com.carfey.(properties/yaml)&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;com.carfey.(properties/yaml)&amp;lt;/code&amp;gt; file in a directory which is explicitly added to the classpath, or at the root of a jar file as is done in &amp;lt;code&amp;gt;obsidian-props.jar&amp;lt;/code&amp;gt; for standalone deployments. Prior to Obsidian 3.6, this file had to exist on the classpath, even if only using the override options below to configure Obsidian.&lt;br /&gt;
&lt;br /&gt;
Instead of using a classpath resource for properties, you can specify an external properties file using the Java system property &amp;lt;code&amp;gt;carfey.properties.file&amp;lt;/code&amp;gt;. All expected properties must be specified in either the default &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; file on the classpath or in the override file. Any properties found in both files will use the override file&#039;s values. Usage: &amp;lt;code&amp;gt;-Dcarfey.properties.file=/home/obsidian/obsidian.properties&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Instead of using a classpath resource for yaml, you can specify an external yaml file using the Java system property &amp;lt;code&amp;gt;carfey.yaml.file&amp;lt;/code&amp;gt;. All expected configuration values must be specified in either the default &amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; file on the classpath or in the override file. Any configuration values found in both files will use the override file&#039;s values. Usage: &amp;lt;code&amp;gt;-Dcarfey.yaml.file=/home/obsidian/obsidian.yaml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 2.1, you may also use a programmatic properties override. Simply call &amp;lt;code&amp;gt;com.carfey.jdk.sys.Configurator.setOverride(Properties props)&amp;lt;/code&amp;gt; as early as possible in the application startup.  To quote the javadoc of this method, &#039;&#039;For use as a programmatic properties override. This must be called before any classes are accessed either through invocation or class initialization that may require access to configuration done through properties. Best if done as early as possible in code, perhaps first in an entry point class that does little else and then hands-off to existing entry point.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 4.4, you may specify configuration values as System Properties or Environment Variables. Duplicated values are resolved as follows:&lt;br /&gt;
# System Property&lt;br /&gt;
# Environment Variable - &#039;&#039;&#039;&#039;&#039;overridden by System Property&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
# Properties - &#039;&#039;&#039;&#039;&#039;overridden by Environment Variable and System Property&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Notifications Configuration ==&lt;br /&gt;
&lt;br /&gt;
Sending notifications requires SMTP configuration to be defined in the Obsidian properties file. The properties file reference at the beginning of this page includes SMTP configuration details.&lt;br /&gt;
&lt;br /&gt;
In addition to the properties file, you can configure some Notifications settings to tweak how your emails are sent. These values are configurable under the &#039;&#039;Dispatch&#039;&#039; category of the [[Admin Scheduler Settings|scheduler settings]] screen. Defaults are provided, but you can update them appropriately for your needs.  &lt;br /&gt;
&lt;br /&gt;
=== Notification sender address and DMARC ===&lt;br /&gt;
&lt;br /&gt;
The notificationFromEmail scheduler setting controls the From address used for notification emails. It is available under:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Scheduler Settings → Dispatch → notificationFromEmail&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Set this to an address belonging to your organization and authorized by the SMTP service configured for your Obsidian installation.&lt;br /&gt;
&lt;br /&gt;
Do not use ops-notification@obsidianscheduler.com when sending through your own SMTP infrastructure. Your SMTP server normally cannot authenticate mail for the obsidianscheduler.com domain. This can cause SPF, DKIM, and DMARC failures and may result in notifications being quarantined or rejected.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
scheduler-notifications@example.com&lt;br /&gt;
&lt;br /&gt;
After updating the value, trigger a test notification and confirm successful receipt.&lt;br /&gt;
&lt;br /&gt;
[[File:NotifSettings_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
== Dependent Libraries ==&lt;br /&gt;
&lt;br /&gt;
Obsidian Scheduler requires a number of third party libraries, both for the web administration application and the scheduler itself.  Below is information on these libraries and how they are used.  Unless otherwise noted, they are mandatory. As of Obsidian 2.5.0, our installer supports selective [[Installation_Guide#Configuring_3rd_Party_Library_Conflict_Management|conflict management]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Authentication (OAuth / OIDC)&#039;&#039;&lt;br /&gt;
All libraries in this section are only required when OAuthAuthenticator is active. &#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* oauth2-oidc-sdk-11.23.1.jar, nimbus-jose-jwt-10.0.2.jar. Nimbus OAuth 2.0 SDK with OpenID Connect extensions and JOSE+JWT support.&lt;br /&gt;
* content-type-2.3.jar, lang-tag-1.7.jar. Supporting libraries for the Nimbus SDK.&lt;br /&gt;
* json-smart-2.5.2.jar, accessors-smart-2.5.2.jar. JSON parsing for the Nimbus SDK.&lt;br /&gt;
* jcip-annotations-1.0-1.jar. Concurrency annotations for the Nimbus SDK.&lt;br /&gt;
* asm-9.7.1.jar. Bytecode manipulation library; transitive dependency of the Nimbus SDK.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Authentication (SAML 2.0)&#039;&#039;&lt;br /&gt;
All libraries in this section are only required when SAMLAuthenticator is active; the installer removes them when SAML is not the selected authentication type. &#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* opensaml-core-4.3.2.jar, opensaml-saml-api-4.3.2.jar, opensaml-saml-impl-4.3.2.jar, opensaml-security-api-4.3.2.jar, opensaml-security-impl-4.3.2.jar, opensaml-xmlsec-api-4.3.2.jar, opensaml-xmlsec-impl-4.3.2.jar, opensaml-messaging-api-4.3.2.jar, opensaml-profile-api-4.3.2.jar, opensaml-soap-api-4.3.2.jar, opensaml-soap-impl-4.3.2.jar, opensaml-storage-api-4.3.2.jar. OpenSAML 4 modules implementing SAML 2.0 messages, metadata and signature handling.&lt;br /&gt;
* java-support-8.4.2.jar. Shibboleth utility library required by OpenSAML.&lt;br /&gt;
* xmlsec-2.3.4.jar. Apache Santuario XML Signature implementation.&lt;br /&gt;
* cryptacular-1.2.5.jar, bcprov-jdk18on-1.85.jar, bcpkix-jdk18on-1.85.jar, bcutil-jdk18on-1.85.jar. Cryptographic providers used for assertion signature verification and SP key handling.&lt;br /&gt;
* velocity-engine-core-2.3.jar. Template engine used by OpenSAML&#039;s message encoders.&lt;br /&gt;
* commons-lang3-3.20.0.jar, commons-codec-1.15.jar. Apache Commons utilities required by OpenSAML.&lt;br /&gt;
* httpclient-4.5.14.jar, httpcore-4.4.16.jar. HTTP client used to retrieve IdP federation metadata.&lt;br /&gt;
* guava-32.1.3-jre.jar, failureaccess-1.0.1.jar, listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar. Guava collections and its companion placeholder artifacts, required by OpenSAML.&lt;br /&gt;
* metrics-core-4.2.25.jar. Dropwizard Metrics, required by OpenSAML.&lt;br /&gt;
* slf4j-api-2.0.12.jar, log4j-slf4j2-impl-2.26.0.jar. SLF4J API and its log4j2 binding, so OpenSAML&#039;s diagnostic logging is routed into Obsidian&#039;s log4j2 configuration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Embedded Tomcat (Obsidian Embedded Tomcat JAR packs only)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* tomcat-embed-core-10.1.39.jar, tomcat-embed-el-10.1.39.jar — Apache Tomcat embedded components (Apache License 2.0). Present in &#039;&#039;&#039;embed-tomcat-lib/&#039;&#039;&#039; when an Embedded Tomcat pack is selected.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Common&#039;&#039;&lt;br /&gt;
* obsidian.jar.  Core Obsidian lib.&lt;br /&gt;
* gson-2.10.1. Supports REST API and web administration.&lt;br /&gt;
* jmustache-1.15.jar. Provides email templating.&lt;br /&gt;
* log4j-api-2.26.0.jar, log4j-core-2.26.0.jar.  Obsidian logging uses log4j2.&lt;br /&gt;
* snakeyaml-2.3.jar. Support for YAML configuration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Databases&#039;&#039;&lt;br /&gt;
* h2-1.4.200.jar.  H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* mariadb-java-client-2.7.2.jar. MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* mssql-jdbc-9.2.1.jre11.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc11-21.1.0.0.jar. Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-42.2.19.jar.  PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Mail&#039;&#039; &lt;br /&gt;
* activation-1.1.jar, javax.mail-1.6.2.jar. Used for javamail (javax) email notifications.&lt;br /&gt;
* angus-activation-2.0.1.jar, angus-mail-2.0.2.jar. Optionally used with Jakarta EE email notifications.&lt;br /&gt;
* jakarta.activation-api-2.1.2.jar, jakarta.mail-api-2.1.2.jar. Used for Jakarta EE email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Scripts&#039;&#039;&lt;br /&gt;
* bsh-2.0b5.jar.  Support for our Bean Shell script execution. (http://www.beanshell.org).  Only required for BeanShellJob usage.&lt;br /&gt;
* jruby-9.2.17.0.jar. Support for Ruby script execution (https://www.jruby.org/). Only required for RubyJob usage.&lt;br /&gt;
* jython-standalone-2.7.2.jar. Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Scripts - Prior to Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Groovy scripting shipped as a single uber archive: apache-groovy-all-4.0.24.jar&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Scripts - As of Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Groovy scripting ships as 13 modular JARs: groovy-4.0.24.jar, groovy-astbuilder-4.0.24.jar, groovy-datetime-4.0.24.jar, groovy-dateutil-4.0.24.jar, groovy-json-4.0.24.jar, groovy-jsr223-4.0.24.jar, groovy-macro-4.0.24.jar, groovy-macro-library-4.0.24.jar, groovy-nio-4.0.24.jar, groovy-sql-4.0.24.jar, groovy-templates-4.0.24.jar, groovy-typecheckers-4.0.24.jar, groovy-xml-4.0.24.jar.&lt;br /&gt;
Plus four owned transitives (included when Groovy is enabled): asm-analysis-9.7.1.jar, asm-tree-9.7.1.jar, asm-util-9.7.1.jar, javaparser-core-3.26.2.jar&lt;br /&gt;
All 17 JARs above are excluded when Groovy scripting is disabled at install time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Web Admin&#039;&#039;&lt;br /&gt;
* dom4j-2.1.3.jar.  XML utilities.&lt;br /&gt;
* flexmark-0.62.2.jar, flexmark-util-0.62.2.jar. Markdown formatting for Description and Parameter job annotations.&lt;br /&gt;
* jxl-2.6.12.jar. Excel file format utilities. Only required in web administration.&lt;br /&gt;
* opencsv-5.4.jar. Support for CSV downloads in UI.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Web Admin - As of Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Markdown rendering for job and parameter descriptions ships as 12 modular JARs:&lt;br /&gt;
flexmark-0.62.2.jar, flexmark-util-ast-0.62.2.jar, flexmark-util-builder-0.62.2.jar, flexmark-util-collection-0.62.2.jar, flexmark-util-data-0.62.2.jar, flexmark-util-dependency-0.62.2.jar, flexmark-util-format-0.62.2.jar, flexmark-util-html-0.62.2.jar, flexmark-util-misc-0.62.2.jar, flexmark-util-options-0.62.2.jar, flexmark-util-sequence-0.62.2.jar, flexmark-util-visitor-0.62.2.jar&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;, the following changes were made:&lt;br /&gt;
* &#039;&#039;&#039;log4j upgraded&#039;&#039;&#039;: log4j-api and log4j-core updated from 2.17.1 to 2.26.0.&lt;br /&gt;
* &#039;&#039;&#039;Modular Flexmark libraries&#039;&#039;&#039;: Obsidian no longer ships &#039;&#039;&#039;flexmark-util-0.62.2.jar&#039;&#039;&#039;. Markdown rendering for job and parameter descriptions now uses 12 modular Flexmark JARs (see &#039;&#039;Web Admin&#039;&#039; above). &lt;br /&gt;
* * &#039;&#039;&#039;Modular Flexmark libraries&#039;&#039;&#039;: Obsidian no longer ships &#039;&#039;&#039;flexmark-util-0.62.2.jar&#039;&#039;&#039;. Web Admin UI rendering now uses 12 modular Flexmark JARs (see &#039;&#039;Scripts&#039;&#039; above). &lt;br /&gt;
* &#039;&#039;&#039;OAuth / OIDC authentication libraries&#039;&#039;&#039; added (Nimbus OAuth 2.0 SDK and transitive dependencies). Required only when OAuthAuthenticator is configured. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties|Authentication (OAuth/OIDC)]] above.&lt;br /&gt;
* &#039;&#039;&#039;SAML 2.0 authentication libraries&#039;&#039;&#039; added (OpenSAML 4 and its transitive dependencies, plus the SLF4J-to-log4j2 binding). Required only when SAMLAuthenticator is configured. See [[Advanced_Configuration#SAML_Authentication_Properties|Authentication (SAML 2.0)]] above.&lt;br /&gt;
* Embedded Tomcat runtime added (tomcat-embed-core-10.1.39.jar, tomcat-embed-el-10.1.39.jar). Present only when an Embedded Tomcat pack is selected. Replaces the Jetty quick-start distribution which has been removed.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 7.0.0&#039;&#039;&#039;, the following libraries were in use.&lt;br /&gt;
* log4j-api-2.17.1.jar, log4j-core-2.17.1.jar.  Obsidian logging uses log4j2.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 6.0.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* groovy-all-3.0.7.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.4.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* gson-2.8.6.jar. Supports REST API and web administration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.2.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* jakarta.activation-1.2.1.jar. Used for email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.0.3&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* &#039;&#039;Obsidian 5.0.2&#039;&#039; log4j-api-2.16.0.jar, log4j-core-2.16.0.jar.  &lt;br /&gt;
* &#039;&#039;Obsidian 5.0.1&#039;&#039; log4j-api-2.15.0.jar, log4j-core-2.15.0.jar.  &lt;br /&gt;
* &#039;&#039;Obsidian 5.0.0&#039;&#039; log4j-api-2.14.1.jar, log4j-core-2.14.1.jar.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.0.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* activation-1.1.jar, javax.mail-1.5.5.jar.  Used for email notifications.&lt;br /&gt;
* dom4j-1.6.1.jar. XML utilities.&lt;br /&gt;
* gson-2.7.jar. Supports REST API and web administration.&lt;br /&gt;
* bsh-2.0b4.jar.  Support for our Bean Shell script execution. (http://www.beanshell.org).  Only required for BeanShellJob usage.&lt;br /&gt;
* groovy-all-2.4.14.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
* jython-standalone-2.7.0.jar. Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
* jruby-complete-9.2.7.0.jar. Support for Ruby script execution (https://www.jruby.org/). Only required for RubyJob usage.&lt;br /&gt;
* jmustache-1.12.jar. Provides email templating.&lt;br /&gt;
* h2-1.4.192.  H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* log4j-1.2.9.jar.  Obsidian logging uses log4j.&lt;br /&gt;
* mariadb-java-client-1.4.5.jar.  MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* jtds-1.3.1.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc7-12.1.0.1.jar.  Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-9.4.1208.jre7.jar. PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
* opencsv-3.8.jar. Support for CSV downloads in UI.&lt;br /&gt;
* flexmark-0.19.6.jar, flexmark-util-0.19.6.jar (as of 4.3.0). Markdown formatting for Description and Parameter job annotations.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 4.5.1&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* groovy-all-2.4.7.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 4.0.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* javax.mail-1.5.2.jar.  Used for email notifications.&lt;br /&gt;
* jstl.jar, standard.jar.  Web utilities. Only required in web administration.&lt;br /&gt;
* gson-2.2.2.jar. Supports REST API and web administration.&lt;br /&gt;
* groovy-all-2.1.8.jar.  Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
* jython-standalone-2.5.3.jar.  Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
* jmustache-1.8.jar. Provides email templating.&lt;br /&gt;
* h2-1.3.154. H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* mariadb-java-client-1.1.5.jar. MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* jtds-1.2.8.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc6-11.2.0.3.jar. Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-9.0-801.jdbc4.jar.  PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.9.1&#039;&#039;&#039;, the following redundant library was in included in the Obsidian packaging.&lt;br /&gt;
* smtp.jar.  Used for email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.2.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* groovy-all-1.7.6.jar&lt;br /&gt;
* jython.jar (version 2.5.2rc2)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.1.1&#039;&#039;&#039;, the following libraries were also included.&lt;br /&gt;
* carfey-date-1.2.jar or carfey-date-1.1.jar.  Date math/manipulation.&lt;br /&gt;
* jdk-gen.jar, jdk.jar, suite-gen.jar, suite.jar.  Core Obsidian libs.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.1.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* ojdbc14.jar Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 1.5&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* gson-1.5.jar. Supports REST API and web administration.&lt;br /&gt;
&lt;br /&gt;
=== Required Libraries for Embedded API ===&lt;br /&gt;
&lt;br /&gt;
To use the [[Embedded_API|Embedded API]], the following resources should be imported. If you have newer versions of the same JARs in your application, feel free to use the newer version. The JAR files below can generally be found in the &#039;&#039;standalone&#039;&#039; directory if your installation. The &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; file can be extracted from the &amp;lt;code&amp;gt;obsidian-properties-configuration.jar&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;obsidian-yaml-configuration.jar&amp;lt;/code&amp;gt; in the &#039;&#039;standalone&#039;&#039; directory, or from an Obsidian WAR file under &amp;lt;code&amp;gt;WEB-INF/classes&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* com.carfey.properties/com.carfey.yaml (configuration file), or obsidian-properties-configuration.jar/obsidian-yaml-configuration.jar, which contains the configuration file&lt;br /&gt;
* obsidian.jar&lt;br /&gt;
* dom4j-2.1.3.jar&lt;br /&gt;
* flexmark-0.62.2.jar plus the modular Flexmark JARs listed under &#039;&#039;Web Admin&#039;&#039; above&lt;br /&gt;
* gson-2.10.1&lt;br /&gt;
* log4j-api-2.26.0.jar, log4j-core-2.26.0.jar&lt;br /&gt;
* Appropriate JDBC JAR (e.g. mariadb-java-client-2.7.2.jar for MySQL or MariaDB)&lt;br /&gt;
&lt;br /&gt;
In addition, the following resources should be included if you wish to have notification support enabled when using the API:&lt;br /&gt;
* jmustache-1.15.jar&lt;br /&gt;
** jakarta.activation-api-2.1.2.jar, jakarta.mail-api-2.1.2.jar. Used for Jakarta EE email notifications.&lt;br /&gt;
** angus-activation-2.0.1.jar, angus-mail-2.0.2.jar. Optionally used with Jakarta EE email notifications.&lt;br /&gt;
** activation-1.1.jar, javax.mail-1.6.2.jar. Used for javamail (javax) email notifications.&lt;br /&gt;
&lt;br /&gt;
Finally, if you need to configure scripting jobs from the API, you will need to include any applicable JARs from the following list:&lt;br /&gt;
* apache-groovy-all-4.0.24.jar&lt;br /&gt;
* bsh-2.0b5.jar&lt;br /&gt;
* jruby-9.2.17.0.jar&lt;br /&gt;
* jython-standalone-2.7.2.jar&lt;br /&gt;
&lt;br /&gt;
== Disabling Job Execution &amp;amp; Scheduling in the Web Application ==&lt;br /&gt;
See the instructions within the [[Getting_Started#Disabling_Job_Scheduling_in_the_Web_Application|Getting Started Guide]].&lt;br /&gt;
&lt;br /&gt;
== Disabling Automatic Database Updates ==&lt;br /&gt;
See the instructions within the [[Getting_Started#Disabling_Automatic_Database_Updates|Getting Started Guide]].&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Advanced_Configuration&amp;diff=4130</id>
		<title>Advanced Configuration</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Advanced_Configuration&amp;diff=4130"/>
		<updated>2026-07-31T02:10:13Z</updated>

		<summary type="html">&lt;p&gt;Craig: /* Dependent Libraries */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian requires some initial configuration parameters for database connection information and desired authentication mechanisms.  The installer takes care of configuring these as provided.  For your reference should you wish to manually create/edit these, note the details below.&lt;br /&gt;
&lt;br /&gt;
== Configuration Details ==&lt;br /&gt;
&lt;br /&gt;
The following sections show the available properties and sample values that can be used in the &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; properties file. While properties format is shown, all the same configuration items apply to yaml support. See [[#Properties.2FYaml_File|Properties/Yaml File]] for more details on this file.&lt;br /&gt;
&lt;br /&gt;
=== Authentication Properties ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Comment out the native authenticator setup&lt;br /&gt;
#com.carfey.suite.security.Authenticator=com.carfey.suite.security.DBAuthenticator&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LDAP Authentication Properties ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#Set your LDAP info here&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.LdapAuthenticator&lt;br /&gt;
# As of Obsidian 2.1.1, you can specify the attribute type used in building up the distinguished name (dn). If unspecified, defaults to cn&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.dn.attribute=uid&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of dn.bases to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.dn.base=ou=people,o=MyOrgHere&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.url=ldap://localhost:10389&lt;br /&gt;
# As of Obsidian 2.1.1, you can specify the SECURITY_AUTHENTICATION. Defaults to simple. &lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.securityAuthentication=simple&lt;br /&gt;
# Any necessary additional information such as Provider, Principal and Host will need to be handled in your custom class extending LdapAuthenticator.&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 3.5.1, you can login via an LDAP attribute that is not part of the dn. A matching entry is found in the LDAP Directory (using anonymous or a fixed lookup account) &lt;br /&gt;
# after which the dn attribute above is retrieved to perform the user&#039;s authentication. The following 5 attributes are only used for this type of lookup and authentication.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.lookupDnAttribute=false&lt;br /&gt;
# As of Obsidian 3.5.1, this is the attribute name that is being searched for in the LDAP directory to build the dn for eventual authentication.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.loginAttribute=sAMAccountName&lt;br /&gt;
# As of Obsidian 3.5.1, this determines if these lookups will be done anonymously (without a session authenticated by user/password)&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.anonymousEnabled=false&lt;br /&gt;
# As of Obsidian 3.5.1, if anonymous lookups are not permitted or desired, provide the dn and password to be used for lookups. Typically, a read-only account with &lt;br /&gt;
# read rights to the LDAP entries and attributes in question is sufficient.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchUserFullDn=cn=ObsidianLDAPReadOnly,ou=people,o=MyOrgHere&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchPassword=password&lt;br /&gt;
# As of Obsidian 4.5.1, an alternate search base configuration parameter is used for the searchUser&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchUserDnBase=ou=people,o=MyOrgHere&lt;br /&gt;
# As of Obsidian 4.7.1, you may specify one or more additional group membership attributes beyond the defaults&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.groupMembershipAttribute=businessGroup~~~departmentName&lt;br /&gt;
&lt;br /&gt;
# Configure who may access the web app, based on LDAP group membership&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.accessDN=cn=SchedulerAccess,ou=groups,o=MyOrgHere&lt;br /&gt;
&lt;br /&gt;
# Configure the Write role used in the admin web app, based on LDAP group membership (by default, users may only read)&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.write.dn=cn=SchedulerWrite,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.write.roleName=Write&lt;br /&gt;
&lt;br /&gt;
# Configure the Admin role used in the admin web app, based on LDAP group membership (users may configure system parameters, etc).&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.admin.dn=cn=SchedulerAdmin,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.admin.roleName=Admin&lt;br /&gt;
&lt;br /&gt;
# Configure the LimitedRead role used in the admin web app, based on LDAP group membership (by default, users may only read)&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.limitedRead.dn=cn=SchedulerLimitedRead,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.limitedRead.roleName=LimitedRead&lt;br /&gt;
&lt;br /&gt;
# Configure the APIrole used by the REST API, based on LDAP group membership&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.api.dn=cn=SchedulerAPI,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.api.roleName=API&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OAuth / OIDC Authentication Properties ====&lt;br /&gt;
&lt;br /&gt;
To use OAuth / OIDC SSO, set the &#039;&#039;Authenticator&#039;&#039; property to&lt;br /&gt;
&#039;&#039;com.carfey.suite.security.OAuthAuthenticator&#039;&#039; and add the properties below. All&lt;br /&gt;
properties are prefixed with &#039;&#039;com.carfey.suite.security.OAuthAuthenticator.&#039;&#039; (shown&lt;br /&gt;
without prefix in the table for brevity).&lt;br /&gt;
&lt;br /&gt;
===== Required properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;issuer&#039;&#039; || OIDC Issuer URL. Obsidian fetches &#039;&#039;&amp;lt;issuer&amp;gt;/.well-known/openid-configuration&#039;&#039; lazily on first use and caches it.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clientId&#039;&#039; || Client ID registered at the IdP.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clientSecret&#039;&#039; || Client secret. Keep out of version control.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;redirectUri&#039;&#039; || Exact callback URL registered at the IdP (e.g. &#039;&#039;https://app.example.com/oauth/callback&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;scopes&#039;&#039; || Space-separated OAuth scopes (e.g. &#039;&#039;openid profile email groups&#039;&#039;). Must include &#039;&#039;openid&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groups&#039;&#039; || Name of the token claim that carries group memberships (e.g. &#039;&#039;groups&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.group&#039;&#039; || IdP group value for this mapping. &#039;&#039;&amp;lt;idx&amp;gt;&#039;&#039; is an arbitrary stable label (numeric or descriptive).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.roleName&#039;&#039; || Obsidian role for this mapping. At least one &#039;&#039;Admin&#039;&#039; mapping is required. Valid roles: &#039;&#039;&#039;Admin&#039;&#039;&#039;, &#039;&#039;&#039;API&#039;&#039;&#039;, &#039;&#039;&#039;Write&#039;&#039;&#039;, &#039;&#039;&#039;LimitedRead&#039;&#039;&#039;, &#039;&#039;&#039;Operator&#039;&#039;&#039;, &#039;&#039;&#039;Author&#039;&#039;&#039;, and folder-based roles (&#039;&#039;&amp;lt;folder&amp;gt;-(Write|Author|Operator)&#039;&#039;).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Optional properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Default !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;displayName&#039;&#039; || (none) || Label on the SSO button: &amp;quot;Sign in with &#039;&#039;&amp;lt;displayName&amp;gt;&#039;&#039;&amp;quot;. Defaults to &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;logout.mode&#039;&#039; || &#039;&#039;local&#039;&#039; || &#039;&#039;local&#039;&#039;: invalidates Obsidian session only. &#039;&#039;rp&#039;&#039;: RP-initiated logout via IdP &#039;&#039;end_session_endpoint&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;postLogoutRedirectUri&#039;&#039; || (none) || Where to redirect after RP-initiated logout. Ignored when &#039;&#039;logout.mode=local&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.user&#039;&#039; || &#039;&#039;sub&#039;&#039; || Claim used as the internal user ID in audit logs. &#039;&#039;&#039;Strongly recommend keeping as &#039;&#039;sub&#039;&#039;&#039;&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.displayName&#039;&#039; || &#039;&#039;preferred_username&#039;&#039; || Claim shown in the Obsidian UI nav bar. Separate from &#039;&#039;claim.user&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.validateTyp&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, rejects Bearer tokens where &#039;&#039;typ&#039;&#039; is present but not &#039;&#039;at+JWT&#039;&#039;. Set &#039;&#039;false&#039;&#039; only for legacy IdPs that omit &#039;&#039;typ&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clockSkewSeconds&#039;&#039; || &#039;&#039;60&#039;&#039; || Clock skew tolerance for &#039;&#039;exp&#039;&#039;/&#039;&#039;nbf&#039;&#039; validation (seconds).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;metadata.ttlHours&#039;&#039; || &#039;&#039;24&#039;&#039; || OIDC discovery document cache TTL in hours.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;expectedAccessTokenAudience&#039;&#039; || &#039;&#039;clientId&#039;&#039; || Expected &#039;&#039;aud&#039;&#039; for Bearer access tokens. See security note below.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsSource&#039;&#039; || &#039;&#039;idtoken&#039;&#039; || Where to read groups during browser SSO: &#039;&#039;idtoken&#039;&#039;, &#039;&#039;accesstoken&#039;&#039;, or &#039;&#039;userinfo&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsBearerSource&#039;&#039; || &#039;&#039;accesstoken&#039;&#039; || Where to read groups on the Bearer path: &#039;&#039;idtoken&#039;&#039; or &#039;&#039;accesstoken&#039;&#039;. &#039;&#039;&#039;userinfo is not permitted on the Bearer path.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsValuePath&#039;&#039; || (none) || Sub-field name when groups is an array of objects (e.g. &#039;&#039;name&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsStripPrefix&#039;&#039; || (none) || Prefix to strip from each group value (e.g. &#039;&#039;/&#039;&#039; for Keycloak full-path style).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsUserinfoTimeout&#039;&#039; || &#039;&#039;5&#039;&#039; || HTTP timeout in seconds for the userinfo endpoint. Only used when &#039;&#039;claim.groupsSource=userinfo&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.prefix&#039;&#039; || (none) || Prefix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.suffix&#039;&#039;. Map is rebuilt at startup; adding a new root folder requires a restart.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.suffix&#039;&#039; || (none) || Suffix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.prefix&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;requireRole&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, browser SSO fails if IdP groups map to no Obsidian roles. Does not apply to REST Bearer tokens.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Security notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;expectedAccessTokenAudience and cross-JWT confusion&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When &#039;&#039;expectedAccessTokenAudience&#039;&#039; is not set it defaults to &#039;&#039;clientId&#039;&#039;. In many OIDC&lt;br /&gt;
configurations the id_token also carries &#039;&#039;aud=clientId&#039;&#039;, which could allow an id_token&lt;br /&gt;
to be submitted as a Bearer token. The &#039;&#039;claim.validateTyp=true&#039;&#039; default mitigates this&lt;br /&gt;
for compliant IdPs (access tokens carry &#039;&#039;typ=at+JWT&#039;&#039;; id_tokens carry &#039;&#039;typ=JWT&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
For maximum defence, set &#039;&#039;expectedAccessTokenAudience&#039;&#039; to a distinct resource-server&lt;br /&gt;
identifier if your IdP supports it.&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;claim.validateTyp=false&#039;&#039; AND &#039;&#039;expectedAccessTokenAudience&#039;&#039; is not explicitly set,&lt;br /&gt;
Obsidian logs a mandatory startup WARN advising you to set &#039;&#039;expectedAccessTokenAudience&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
===== Keycloak example =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.OAuthAuthenticator&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.issuer=http://localhost:8081/realms/obsidian&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.clientId=obsidian-client&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.clientSecret=test-client-secret-local&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.redirectUri=http://localhost:8080/oauth/callback&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.scopes=openid profile email groups&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.claim.groups=groups&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.claim.groupsSource=idtoken&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.1.group=Obsidian-Admins&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.1.roleName=Admin&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.2.group=Obsidian-Write&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.2.roleName=Write&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.3.group=Obsidian-Read&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.3.roleName=LimitedRead&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.4.group=Obsidian-API&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.4.roleName=API&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.displayName=Keycloak&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.logout.mode=local&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.requireRole=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Provider notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Microsoft Entra ID&#039;&#039;&#039;: groups claim contains GUIDs by default; configure Optional&lt;br /&gt;
Claims for display names. Accounts with 200+ groups trigger overage. Multi-tenant apps not&lt;br /&gt;
supported in v1.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auth0&#039;&#039;&#039;: opaque access tokens (Auth0 default) are not supported on the Bearer path.&lt;br /&gt;
Configure a custom API audience to get JWT access tokens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Okta&#039;&#039;&#039;: use the Custom Authorization Server URL, not the Org Authorization Server.&lt;br /&gt;
&lt;br /&gt;
===== v1 limitations =====&lt;br /&gt;
&lt;br /&gt;
* Opaque access tokens not supported on the REST/Bearer path.&lt;br /&gt;
* No &#039;&#039;jti&#039;&#039; replay protection.&lt;br /&gt;
* Microsoft Entra multi-tenant apps not supported.&lt;br /&gt;
* No post-login IdP session validity check.&lt;br /&gt;
* Dynamic role map requires restart for new root folders.&lt;br /&gt;
* Installer UI supports up to 10 static role pairs; runtime supports unlimited.&lt;br /&gt;
&lt;br /&gt;
==== SAML Authentication Properties ====&lt;br /&gt;
&lt;br /&gt;
To use SAML 2.0 SSO, set the &#039;&#039;Authenticator&#039;&#039; property to&lt;br /&gt;
&#039;&#039;com.carfey.suite.security.saml.SAMLAuthenticator&#039;&#039; and add the properties below. All&lt;br /&gt;
properties are prefixed with &#039;&#039;com.carfey.suite.security.saml.SAMLAuthenticator.&#039;&#039; (shown&lt;br /&gt;
without prefix in the table for brevity). Note the &#039;&#039;.saml.&#039;&#039; package segment in both values.&lt;br /&gt;
&lt;br /&gt;
Obsidian acts as a SAML Service Provider and supports SP-initiated browser login only. See&lt;br /&gt;
[[Authenticator#SAMLAuthenticator_(SAML_2.0_SSO)|Authenticator — SAMLAuthenticator]] for how the&lt;br /&gt;
login and logout flows behave and for the list of &#039;&#039;/saml&#039;&#039; endpoints.&lt;br /&gt;
&lt;br /&gt;
===== Required properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;entityId&#039;&#039; || SP entity ID registered at the IdP (e.g. &#039;&#039;https://app.example.com/obsidian&#039;&#039;). Obsidian also requires this value to appear in the assertion&#039;s &#039;&#039;AudienceRestriction&#039;&#039; when the IdP sends one.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;assertionConsumerServiceUrl&#039;&#039; || Exact ACS URL where the IdP posts responses. This must be Obsidian&#039;s callback endpoint, e.g. &#039;&#039;https://app.example.com/obsidian/saml/callback&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;idpMetadataUrl&#039;&#039; &#039;&#039;&#039;or&#039;&#039;&#039; &#039;&#039;idpMetadataFile&#039;&#039; || Source of the IdP federation metadata: an HTTP(S) URL, or an absolute path to a local XML file. Set exactly one of the two.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups&#039;&#039; || Name of the assertion attribute carrying group memberships (e.g. &#039;&#039;groups&#039;&#039;). Required unless &#039;&#039;dynamicRole.prefix&#039;&#039; is set instead.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.group&#039;&#039; || IdP group value for this mapping. &#039;&#039;&amp;lt;idx&amp;gt;&#039;&#039; is an arbitrary stable label (numeric or descriptive). Not needed if all roles come from &#039;&#039;dynamicRole&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.roleName&#039;&#039; || Obsidian role for this mapping. The installer requires at least one &#039;&#039;Admin&#039;&#039; mapping, and you need one in practice so that somebody can administer Obsidian. Valid roles: &#039;&#039;&#039;Admin&#039;&#039;&#039;, &#039;&#039;&#039;Write&#039;&#039;&#039;, &#039;&#039;&#039;LimitedRead&#039;&#039;&#039;, &#039;&#039;&#039;Operator&#039;&#039;&#039;, &#039;&#039;&#039;Author&#039;&#039;&#039;, and folder-based roles (&#039;&#039;&amp;lt;folder&amp;gt;-(Write|Author|Operator)&#039;&#039;). The &#039;&#039;API&#039;&#039; role has no effect on a SAML install, because the REST API is unavailable.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Because &#039;&#039;signRequests&#039;&#039; defaults to enabled, a default configuration also requires the three&lt;br /&gt;
&#039;&#039;keystore&#039;&#039; properties below. Disable &#039;&#039;signRequests&#039;&#039; only if your IdP does not require signed&lt;br /&gt;
authentication requests.&lt;br /&gt;
&lt;br /&gt;
===== Optional properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Default !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;displayName&#039;&#039; || (none) || Label on the SSO button: &amp;quot;Sign in with &#039;&#039;&amp;lt;displayName&amp;gt;&#039;&#039;&amp;quot;. Defaults to &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;signRequests&#039;&#039; || &#039;&#039;true&#039;&#039; || Signs the outbound &#039;&#039;AuthnRequest&#039;&#039; and SP &#039;&#039;LogoutRequest&#039;&#039;. Requires the three &#039;&#039;keystore&#039;&#039; properties.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.path&#039;&#039; || (none) || Path to the keystore holding the SP signing key. Required when &#039;&#039;signRequests&#039;&#039; is enabled.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.password&#039;&#039; || (none) || Keystore password. Keep out of version control.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.alias&#039;&#039; || (none) || Alias of the SP signing key within the keystore.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;wantAssertionsSigned&#039;&#039; || &#039;&#039;true&#039;&#039; || Requires the IdP to sign assertions, verified against the signing certificate in the IdP metadata.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;singleLogoutServiceUrl&#039;&#039; || (none) || SP single logout URL. When set it is published in Obsidian&#039;s SP metadata and is matched against the &#039;&#039;Destination&#039;&#039; of an incoming &#039;&#039;LogoutRequest&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;logout.mode&#039;&#039; || &#039;&#039;local&#039;&#039; || &#039;&#039;local&#039;&#039;: invalidates the Obsidian session only. &#039;&#039;slo&#039;&#039;: SP-initiated single logout via the IdP&#039;s HTTP-Redirect &#039;&#039;SingleLogoutService&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;postLogoutRedirectUri&#039;&#039; || (none) || Where to redirect after the IdP&#039;s &#039;&#039;LogoutResponse&#039;&#039;. When blank the browser returns to &#039;&#039;/&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;nameIdFormat&#039;&#039; || &#039;&#039;urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified&#039;&#039; || &#039;&#039;NameIDPolicy&#039;&#039; format requested from the IdP. A transient format logs a startup warning, because transient NameIDs cannot serve as stable user IDs.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.user&#039;&#039; || &#039;&#039;nameId&#039;&#039; || Assertion attribute used as the internal user ID in audit logs. &#039;&#039;&#039;Strongly recommend keeping as &#039;&#039;nameId&#039;&#039;&#039;&#039;&#039;; any other value logs a startup warning.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.displayName&#039;&#039; || (none) || Attribute shown in the Obsidian UI nav bar. Separate from &#039;&#039;attribute.user&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.friendlyName&#039;&#039; || (none) || Match the groups attribute on its &#039;&#039;FriendlyName&#039;&#039; instead of its &#039;&#039;Name&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.valuePath&#039;&#039; || (none) || Sub-field name when the groups attribute holds structured values.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.stripPrefix&#039;&#039; || (none) || Prefix to strip from each group value (e.g. &#039;&#039;/&#039;&#039; for Keycloak full-path style).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;metadata.ttlHours&#039;&#039; || &#039;&#039;24&#039;&#039; || IdP federation metadata cache TTL in hours. A failed refresh falls back to the cached copy with a warning.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clockSkewSeconds&#039;&#039; || &#039;&#039;60&#039;&#039; || Clock skew tolerance for &#039;&#039;NotBefore&#039;&#039; / &#039;&#039;NotOnOrAfter&#039;&#039; validation (seconds).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;forceAuthn&#039;&#039; || &#039;&#039;false&#039;&#039; || Sets &#039;&#039;ForceAuthn=&amp;quot;true&amp;quot;&#039;&#039; on the &#039;&#039;AuthnRequest&#039;&#039;, asking the IdP to reauthenticate the user.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;authnContextClassRef&#039;&#039; || (none) || Requests a specific &#039;&#039;RequestedAuthnContext&#039;&#039;. A differing context in the response is logged as a warning, not rejected.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.prefix&#039;&#039; || (none) || Prefix for auto-generated folder-role group mappings. Usable on its own. The map is built at startup, so adding a new root job folder requires a restart.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.suffix&#039;&#039; || (none) || Optional suffix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.prefix&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;requireRole&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, browser SSO fails if the user&#039;s group attributes map to no Obsidian roles.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Security notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Encrypted assertions are not supported&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Obsidian requires signed, unencrypted assertions. If your SP metadata carries no signing key&lt;br /&gt;
material, Obsidian logs a startup warning: IdPs such as AD FS may then choose to encrypt the&lt;br /&gt;
assertion, which this release cannot process. Configure the SP keystore and register the&lt;br /&gt;
generated metadata so the IdP sees a signing &#039;&#039;KeyDescriptor&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Do not disable both signature settings&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Setting &#039;&#039;signRequests&#039;&#039; and &#039;&#039;wantAssertionsSigned&#039;&#039; both to &#039;&#039;false&#039;&#039; is accepted but logs a&lt;br /&gt;
startup warning. In that state assertions are neither signed nor verified, which is not safe for&lt;br /&gt;
production.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SP-initiated flows and replay&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Obsidian issues an &#039;&#039;AuthnRequest&#039;&#039; and tracks its ID in the browser session for five minutes.&lt;br /&gt;
When the IdP&#039;s response carries &#039;&#039;InResponseTo&#039;&#039;, it must match one of those pending IDs and is&lt;br /&gt;
consumed on use, so the same response cannot be replayed. There is no persistent store of&lt;br /&gt;
processed assertion IDs, so keep assertion lifetimes short at the IdP.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Clustered deployments and IdP-initiated logout&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
An IdP-initiated &#039;&#039;LogoutRequest&#039;&#039; can only end the Obsidian session on the node that receives&lt;br /&gt;
it. Use sticky sessions if you rely on single logout across a cluster.&lt;br /&gt;
&lt;br /&gt;
===== Keycloak example =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.saml.SAMLAuthenticator&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.entityId=https://app.example.com/obsidian&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.assertionConsumerServiceUrl=https://app.example.com/obsidian/saml/callback&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.idpMetadataUrl=https://keycloak.example.com/realms/obsidian/protocol/saml/descriptor&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.path=/opt/obsidian/saml-sp.p12&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.password=changeit&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.alias=obsidian-sp&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.attribute.groups=groups&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.1.group=Obsidian-Admins&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.1.roleName=Admin&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.2.group=Obsidian-Write&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.2.roleName=Write&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.3.group=Obsidian-Read&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.3.roleName=LimitedRead&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.displayName=Keycloak&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.logout.mode=local&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.requireRole=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Provider notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Microsoft Entra ID&#039;&#039;&#039;: group claims are omitted from SAML assertions once an account belongs to&lt;br /&gt;
roughly 150 groups. Filter the groups sent to the application, or map app roles instead. Note this&lt;br /&gt;
threshold differs from the OIDC one described above.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AD FS&#039;&#039;&#039;: the federation metadata document is normally at&lt;br /&gt;
&#039;&#039;/FederationMetadata/2007-06/FederationMetadata.xml&#039;&#039;. Register signing-capable SP metadata so AD FS&lt;br /&gt;
does not encrypt the assertion, which Obsidian cannot decrypt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Okta&#039;&#039;&#039;: use the application&#039;s SAML metadata URL for &#039;&#039;idpMetadataUrl&#039;&#039;. Okta&#039;s logout endpoints&lt;br /&gt;
may be POST-only; if no HTTP-Redirect &#039;&#039;SingleLogoutService&#039;&#039; is advertised, &#039;&#039;logout.mode=slo&#039;&#039;&lt;br /&gt;
falls back to local logout.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auth0&#039;&#039;&#039;: federation metadata is served at &#039;&#039;samlp/metadata/&amp;lt;client-id&amp;gt;&#039;&#039;. Group memberships must&lt;br /&gt;
be added to the assertion with an Auth0 Action or rule.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Keycloak and other generic IdPs&#039;&#039;&#039;: Keycloak publishes a SAML descriptor at&lt;br /&gt;
&#039;&#039;/realms/&amp;lt;realm&amp;gt;/protocol/saml/descriptor&#039;&#039;. Any IdP that publishes SAML 2.0 metadata with an&lt;br /&gt;
HTTP-POST &#039;&#039;AssertionConsumerService&#039;&#039; target and a signing certificate will work.&lt;br /&gt;
&lt;br /&gt;
===== v1 limitations =====&lt;br /&gt;
&lt;br /&gt;
* The REST API is unavailable while &#039;&#039;SAMLAuthenticator&#039;&#039; is active — there is no Bearer or JWT equivalent.&lt;br /&gt;
* SP-initiated login only.&lt;br /&gt;
* Encrypted assertions are not supported.&lt;br /&gt;
* No persistent assertion replay store beyond the single-use &#039;&#039;InResponseTo&#039;&#039; match and the assertion time window.&lt;br /&gt;
* Dynamic role map (&#039;&#039;dynamicRole.prefix&#039;&#039;/&#039;&#039;suffix&#039;&#039;) requires a server restart when new root job folders are added.&lt;br /&gt;
* Installer UI supports up to 10 static role pairs; the runtime supports unlimited.&lt;br /&gt;
&lt;br /&gt;
=== SMTP Mail Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Email configuration for notifications, if desired&lt;br /&gt;
#for straight up open relay, just specify the host using&lt;br /&gt;
mail.smtp.host=smtp.myopenrelayhost.com&lt;br /&gt;
mail.smtp.port=port &lt;br /&gt;
#(standard ports are 25, 465 for SSL, 587 for TLS)&lt;br /&gt;
&lt;br /&gt;
#for using TLS and SSL, provide these as necessary&lt;br /&gt;
mail.smtp.socketFactory.port=port&lt;br /&gt;
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory&lt;br /&gt;
mail.smtp.auth=true&lt;br /&gt;
mail.smtp.user=myauthenticatedemailuser@email.com&lt;br /&gt;
mail.smtp.password=PASSWORD&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 2.5.0, we support JNDI lookups for email sessions. All other email properties can be excluded.&lt;br /&gt;
mail.session.jndi.path=java:comp/env/mail/session&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Database Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Database configuration&lt;br /&gt;
com.carfey.obsidian.db.url=jdbc:mysql://localhost/obsidian&lt;br /&gt;
com.carfey.obsidian.db.userId=user&lt;br /&gt;
com.carfey.obsidian.db.password=pass&lt;br /&gt;
&lt;br /&gt;
# Sample JDBC URL formats for all platforms&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:mysql://localhost:3306/obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:h2:C:/dev/workspace/obsidian;MVCC=TRUE&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:oracle:thin:@localhost:1521:obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:postgresql://localhost:5432/obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:sqlserver://localhost:1433;databaseName=obsidian&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 6.3.0, we support constructing simple JDBC urls from component properties&lt;br /&gt;
# Only supports basic URL formats noted above and does not support H2&lt;br /&gt;
#com.carfey.obsidian.db.host=localhost&lt;br /&gt;
#com.carfey.obsidian.db.port=3306&lt;br /&gt;
#com.carfey.obsidian.db.databaseName=obsidianDB&lt;br /&gt;
#com.carfey.obsidian.db.dbType=mariadb/mysql/oracle/postgresql/sqlserver&lt;br /&gt;
#com.carfey.obsidian.db.oracleSid=SIDORCL&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 2.5.0, we support JNDI lookups for database connectivity.&lt;br /&gt;
com.carfey.obsidian.db.url=java:comp/env/jdbc/obsidian&lt;br /&gt;
com.carfey.obsidian.db.jndiType=mysql&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=mysql is also used for MariaDB&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=oracle&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=postgresql&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=h2&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=sqlserver&lt;br /&gt;
&lt;br /&gt;
# Table prefix may be used to add a prefix to tables and related database objects. It must be 6 or fewer characters and can contain letters or underscores. Available as of version 2.0.&lt;br /&gt;
com.carfey.obsidian.db.tablePrefix=OBSDN_&lt;br /&gt;
&lt;br /&gt;
# For Oracle databases, when Obsidian&#039;s tables exist in a different schema from the user specified above, or if the user does not default to the schema matching its name, specify the target schema here. Available as of version 2.1.&lt;br /&gt;
# For details on the required privileges to run with an alternate user, see https://wiki.obsidianscheduler.com/docs/Obsidian_Tables#Oracle_Privileges&lt;br /&gt;
# This same configuration item can be used to configure the Postgresql schema. If unspecified, the default is public. Available as of Obsidian 2.1.1. &lt;br /&gt;
# If using JNDI and Oracle/Postgresql, we recommend you set this value to ensure best performance.&lt;br /&gt;
com.carfey.obsidian.db.schema=obsidian&lt;br /&gt;
&lt;br /&gt;
#default max connections per pool if not specified is 50&lt;br /&gt;
com.carfey.obsidian.db.maxConnections=40&lt;br /&gt;
&lt;br /&gt;
#default millis timeout to retrieve available connection from pool if not specified is 2000&lt;br /&gt;
com.carfey.obsidian.db.connectionTimeout=2000&lt;br /&gt;
&lt;br /&gt;
# As of 3.4.0, you can disable pooling of connections, which is suggested only if using another connection pool through JNDI. When true, maxConnections is ignored. &lt;br /&gt;
# When a JNDI data source is selected in the installer, this is set to true by default.&lt;br /&gt;
com.carfey.obsidian.db.disablePooling=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.3.0, Oracle database supports configured wait time.&lt;br /&gt;
com.carfey.obsidian.db.timedLockWaitEnabled=true (default)&lt;br /&gt;
com.carfey.obsidian.db.timedLockWaitSeconds=1 (default, prior to 4.3.0, indefinite wait)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Logging Properties ===&lt;br /&gt;
As of &#039;&#039;&#039;Obsidian 5.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Please see [https://logging.apache.org/log4j/2.x/manual/configuration.html Log4j2 configuration]&lt;br /&gt;
&lt;br /&gt;
Before &#039;&#039;&#039;Obsidian 5.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Please see [https://logging.apache.org/log4j/1.2/manual.html Log4j Configuration]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Miscellaneous Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# If you wish to change the tokens used to reference global parameters in your job configuration, use the following two values:&lt;br /&gt;
global.param.start.token={{&lt;br /&gt;
global.param.end.token=}}&lt;br /&gt;
&lt;br /&gt;
# Populated by the installer, these values can be used to initialize licence key information into the database.&lt;br /&gt;
com.carfey.obsidian.licence.key=licenceKey&lt;br /&gt;
# Name is only required for hardware-linked or site licenses. Use the name exactly as provided by Carfey Software Corporation in your license email.&lt;br /&gt;
com.carfey.obsidian.licence.name=licenceeName&lt;br /&gt;
&lt;br /&gt;
# As of 2.8.0, if you have issues with classloading (e.g. in Grails or Spring), this flag enables usage of the context classloader .&lt;br /&gt;
com.carfey.jdk.useContextClassLoader=true&lt;br /&gt;
&lt;br /&gt;
# As of 2.9.0, you can set the scheduler host designator name multiple times in the same JVM by using this parameter:&lt;br /&gt;
com.carfey.obsidian.schedulerDesignation=obsidian-dev&lt;br /&gt;
&lt;br /&gt;
# As of 3.0.0, Job Forking can be enabled and configured per node&lt;br /&gt;
com.carfey.obsidian.jvmJobForkingEnabledOnThisNode=true&lt;br /&gt;
# This property is the location of the fork scripts. Obsidian is bundled with obsidianForkedJob.bat and obsidianForkedJob.sh. These are the expected script names.&lt;br /&gt;
com.carfey.obsidian.forkedJobScriptLocation=/Obsidian-3.0.0&lt;br /&gt;
# Use the following optional property if you need to override the default classpath that is built using the contents of the standalone directory.&lt;br /&gt;
# This allows for job forking support in embedded and even webapp deployments. Use the classpath format supported by your operating system.&lt;br /&gt;
#com.carfey.obsidian.forkedJobscriptClasspathOverride=&lt;br /&gt;
# Use the following optional property if you wish to include database configuration parameters as arguments passed to the forking scripts and the target forked Obsidian class.&lt;br /&gt;
# For example, you may need to do this if your configuration is done via System Property overrides or programmmatically in your embedded Obsidian instance.&lt;br /&gt;
#com.carfey.obsidian.jvmJobForkingIncludeDbSysParms=true&lt;br /&gt;
&lt;br /&gt;
# Obsolete as of 4.0.0. As of 3.4.0, the base URL used to resolve links in the Obsidian web app can be overridden. This is typically only required when a load balancer or proxy is used to access Obsidian.&lt;br /&gt;
com.carfey.obsidian.baseHrefUrl=http://myhost:8080/obsidian&lt;br /&gt;
&lt;br /&gt;
# As of 4.3.0, description/parameter formatting is supported. Custom formatter (com.carfey.ops.job.config.formatter.Formatter) supported via this parameter.&lt;br /&gt;
com.carfey.obsidian.formatterClass=com.carfey.ops.job.config.formatter.MarkdownFormatter (default)&lt;br /&gt;
&lt;br /&gt;
# As of 4.4.0, support for running DDL outside Obsidian and applying only the data portion of upgrades. May require incremental upgrades. Contact support for assistance. Enabled with the following:&lt;br /&gt;
com.carfey.obsidian.runner.skipDDL=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.5.0, Slack notifications can be automatically configured for FATAL, ERROR and WARN events by setting this property with a Slack incoming web hook URL. The user name property is optional and defaults to Obsidian Scheduler&lt;br /&gt;
com.carfey.obsidian.slack.webhookUrl=https://hooks.slack.com/services/XXXXXXX/YYYYYYYY/ZZZZZZZZZZ&lt;br /&gt;
com.carfey.obsidian.slack.userName=Obsidian Scheduler (prod)&lt;br /&gt;
&lt;br /&gt;
# As of 4.6.0, Obsidian supports starting a node in paused state. This can also be set as a System property.&lt;br /&gt;
com.carfey.obsidian.schedulerPausedOnStartup=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.10.0, Obsidian supports disabling script engine jobs selectively.&lt;br /&gt;
com.carfey.ops.job.script.BeanShellJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.GroovyJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.RubyJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.PythonJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.JavaScriptJob.enabled=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.0.0, Signal interrupt (supported as of 4.5.1) is disabled by default. Use configuration to enable.&lt;br /&gt;
com.carfey.obsidian.signalHandlerActiveForScheduler=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.0, you can send Obsidian events to a REST endpoint via an out-of-the-box Event Hook. The basicAuthorization property is optional, the sample showing its use for myusername:mypassword. The &amp;quot;Basic &amp;quot; prefix is added by Obsidian.&lt;br /&gt;
com.carfey.obsidian.restfulEventHook.url=https://myendpoint.com:1234/rest/obsidian_events&lt;br /&gt;
com.carfey.obsidian.restfulEventHook.basicAuthorization=bXl1c2VybmFtZTpteXBhc3N3b3Jk&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.1, maintenance jobs are automatically scheduled for new installations. Set the following property to disable this behaviour.&lt;br /&gt;
com.carfey.obsidian.skipMaintenanceJobInitialization=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.1, you can send Obsidian events to standard output and error streams using an out-of-the-box Event Hook. You can enable the default behaviour and auto-register it by adding the following configuration.&lt;br /&gt;
com.carfey.obsidian.standardOutputStreamsEventHook.enabled=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Table Prefixes ===&lt;br /&gt;
As shown in full configuration reference, a table name prefix may be specified to create Obsidian tables with names beginning with a specified string.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Table prefix may be used to add a prefix to tables and related database objects. It must be 6 or fewer characters and can contain letters or underscores. Available as of version 2.0.&lt;br /&gt;
com.carfey.obsidian.db.tablePrefix=OBSDN_&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Properties/Yaml File ==&lt;br /&gt;
&lt;br /&gt;
The above configuration must reside in a properties file named &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; or a yaml file named &amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; found according to the rules of [http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String) ClassLoader.getResource].  If both yaml and properties files are found, yaml is loaded first and properties values will override. Some possibilities include &amp;lt;code&amp;gt;WEB-INF/classes/com.carfey.(properties/yaml)&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;com.carfey.(properties/yaml)&amp;lt;/code&amp;gt; file in a directory which is explicitly added to the classpath, or at the root of a jar file as is done in &amp;lt;code&amp;gt;obsidian-props.jar&amp;lt;/code&amp;gt; for standalone deployments. Prior to Obsidian 3.6, this file had to exist on the classpath, even if only using the override options below to configure Obsidian.&lt;br /&gt;
&lt;br /&gt;
Instead of using a classpath resource for properties, you can specify an external properties file using the Java system property &amp;lt;code&amp;gt;carfey.properties.file&amp;lt;/code&amp;gt;. All expected properties must be specified in either the default &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; file on the classpath or in the override file. Any properties found in both files will use the override file&#039;s values. Usage: &amp;lt;code&amp;gt;-Dcarfey.properties.file=/home/obsidian/obsidian.properties&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Instead of using a classpath resource for yaml, you can specify an external yaml file using the Java system property &amp;lt;code&amp;gt;carfey.yaml.file&amp;lt;/code&amp;gt;. All expected configuration values must be specified in either the default &amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; file on the classpath or in the override file. Any configuration values found in both files will use the override file&#039;s values. Usage: &amp;lt;code&amp;gt;-Dcarfey.yaml.file=/home/obsidian/obsidian.yaml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 2.1, you may also use a programmatic properties override. Simply call &amp;lt;code&amp;gt;com.carfey.jdk.sys.Configurator.setOverride(Properties props)&amp;lt;/code&amp;gt; as early as possible in the application startup.  To quote the javadoc of this method, &#039;&#039;For use as a programmatic properties override. This must be called before any classes are accessed either through invocation or class initialization that may require access to configuration done through properties. Best if done as early as possible in code, perhaps first in an entry point class that does little else and then hands-off to existing entry point.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 4.4, you may specify configuration values as System Properties or Environment Variables. Duplicated values are resolved as follows:&lt;br /&gt;
# System Property&lt;br /&gt;
# Environment Variable - &#039;&#039;&#039;&#039;&#039;overridden by System Property&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
# Properties - &#039;&#039;&#039;&#039;&#039;overridden by Environment Variable and System Property&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Notifications Configuration ==&lt;br /&gt;
&lt;br /&gt;
Sending notifications requires SMTP configuration to be defined in the Obsidian properties file. The properties file reference at the beginning of this page includes SMTP configuration details.&lt;br /&gt;
&lt;br /&gt;
In addition to the properties file, you can configure some Notifications settings to tweak how your emails are sent. These values are configurable under the &#039;&#039;Dispatch&#039;&#039; category of the [[Admin Scheduler Settings|scheduler settings]] screen. Defaults are provided, but you can update them appropriately for your needs.  &lt;br /&gt;
&lt;br /&gt;
=== Notification sender address and DMARC ===&lt;br /&gt;
&lt;br /&gt;
The notificationFromEmail scheduler setting controls the From address used for notification emails. It is available under:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Scheduler Settings → Dispatch → notificationFromEmail&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Set this to an address belonging to your organization and authorized by the SMTP service configured for your Obsidian installation.&lt;br /&gt;
&lt;br /&gt;
Do not use ops-notification@obsidianscheduler.com when sending through your own SMTP infrastructure. Your SMTP server normally cannot authenticate mail for the obsidianscheduler.com domain. This can cause SPF, DKIM, and DMARC failures and may result in notifications being quarantined or rejected.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
scheduler-notifications@example.com&lt;br /&gt;
&lt;br /&gt;
After updating the value, trigger a test notification and confirm successful receipt.&lt;br /&gt;
&lt;br /&gt;
[[File:NotifSettings_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
== Dependent Libraries ==&lt;br /&gt;
&lt;br /&gt;
Obsidian Scheduler requires a number of third party libraries, both for the web administration application and the scheduler itself.  Below is information on these libraries and how they are used.  Unless otherwise noted, they are mandatory. As of Obsidian 2.5.0, our installer supports selective [[Installation_Guide#Configuring_3rd_Party_Library_Conflict_Management|conflict management]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Authentication (OAuth / OIDC)&#039;&#039;&lt;br /&gt;
All libraries in this section are only required when OAuthAuthenticator is active. &#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* oauth2-oidc-sdk-11.23.1.jar, nimbus-jose-jwt-10.0.2.jar. Nimbus OAuth 2.0 SDK with OpenID Connect extensions and JOSE+JWT support.&lt;br /&gt;
* content-type-2.3.jar, lang-tag-1.7.jar. Supporting libraries for the Nimbus SDK.&lt;br /&gt;
* json-smart-2.5.2.jar, accessors-smart-2.5.2.jar. JSON parsing for the Nimbus SDK.&lt;br /&gt;
* jcip-annotations-1.0-1.jar. Concurrency annotations for the Nimbus SDK.&lt;br /&gt;
* asm-9.7.1.jar. Bytecode manipulation library; transitive dependency of the Nimbus SDK.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Authentication (SAML 2.0)&#039;&#039;&lt;br /&gt;
All libraries in this section are only required when SAMLAuthenticator is active; the installer removes them when SAML is not the selected authentication type. &#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* opensaml-core-4.3.2.jar, opensaml-saml-api-4.3.2.jar, opensaml-saml-impl-4.3.2.jar, opensaml-security-api-4.3.2.jar, opensaml-security-impl-4.3.2.jar, opensaml-xmlsec-api-4.3.2.jar, opensaml-xmlsec-impl-4.3.2.jar, opensaml-messaging-api-4.3.2.jar, opensaml-profile-api-4.3.2.jar, opensaml-soap-api-4.3.2.jar, opensaml-soap-impl-4.3.2.jar, opensaml-storage-api-4.3.2.jar. OpenSAML 4 modules implementing SAML 2.0 messages, metadata and signature handling.&lt;br /&gt;
* java-support-8.4.2.jar. Shibboleth utility library required by OpenSAML.&lt;br /&gt;
* xmlsec-2.3.4.jar. Apache Santuario XML Signature implementation.&lt;br /&gt;
* cryptacular-1.2.5.jar, bcprov-jdk18on-1.85.jar, bcpkix-jdk18on-1.85.jar, bcutil-jdk18on-1.85.jar. Cryptographic providers used for assertion signature verification and SP key handling.&lt;br /&gt;
* velocity-engine-core-2.3.jar. Template engine used by OpenSAML&#039;s message encoders.&lt;br /&gt;
* commons-lang3-3.20.0.jar, commons-codec-1.15.jar. Apache Commons utilities required by OpenSAML.&lt;br /&gt;
* httpclient-4.5.14.jar, httpcore-4.4.16.jar. HTTP client used to retrieve IdP federation metadata.&lt;br /&gt;
* guava-32.1.3-jre.jar, failureaccess-1.0.1.jar, listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar. Guava collections and its companion placeholder artifacts, required by OpenSAML.&lt;br /&gt;
* metrics-core-4.2.25.jar. Dropwizard Metrics, required by OpenSAML.&lt;br /&gt;
* slf4j-api-2.0.12.jar, log4j-slf4j2-impl-2.26.0.jar. SLF4J API and its log4j2 binding, so OpenSAML&#039;s diagnostic logging is routed into Obsidian&#039;s log4j2 configuration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Embedded Tomcat (Obsidian Embedded Tomcat JAR packs only)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* tomcat-embed-core-10.1.39.jar, tomcat-embed-el-10.1.39.jar — Apache Tomcat embedded components (Apache License 2.0). Present in &#039;&#039;&#039;embed-tomcat-lib/&#039;&#039;&#039; when an Embedded Tomcat pack is selected.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Common&#039;&#039;&lt;br /&gt;
* obsidian.jar.  Core Obsidian lib.&lt;br /&gt;
* gson-2.10.1. Supports REST API and web administration.&lt;br /&gt;
* jmustache-1.15.jar. Provides email templating.&lt;br /&gt;
* log4j-api-2.26.0.jar, log4j-core-2.26.0.jar.  Obsidian logging uses log4j2.&lt;br /&gt;
* snakeyaml-2.3.jar. Support for YAML configuration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Databases&#039;&#039;&lt;br /&gt;
* h2-1.4.200.jar.  H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* mariadb-java-client-2.7.2.jar. MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* mssql-jdbc-9.2.1.jre11.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc11-21.1.0.0.jar. Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-42.2.19.jar.  PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Mail&#039;&#039; &lt;br /&gt;
* activation-1.1.jar, javax.mail-1.6.2.jar. Used for javamail (javax) email notifications.&lt;br /&gt;
* angus-activation-2.0.1.jar, angus-mail-2.0.2.jar. Optionally used with Jakarta EE email notifications.&lt;br /&gt;
* jakarta.activation-api-2.1.2.jar, jakarta.mail-api-2.1.2.jar. Used for Jakarta EE email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Scripts&#039;&#039;&lt;br /&gt;
* bsh-2.0b5.jar.  Support for our Bean Shell script execution. (http://www.beanshell.org).  Only required for BeanShellJob usage.&lt;br /&gt;
* jruby-9.2.17.0.jar. Support for Ruby script execution (https://www.jruby.org/). Only required for RubyJob usage.&lt;br /&gt;
* jython-standalone-2.7.2.jar. Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Scripts - Prior to Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Groovy scripting shipped as a single uber archive: apache-groovy-all-4.0.24.jar&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Scripts - As of Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Groovy scripting ships as 13 modular JARs: groovy-4.0.24.jar, groovy-astbuilder-4.0.24.jar, groovy-datetime-4.0.24.jar, groovy-dateutil-4.0.24.jar, groovy-json-4.0.24.jar, groovy-jsr223-4.0.24.jar, groovy-macro-4.0.24.jar, groovy-macro-library-4.0.24.jar, groovy-nio-4.0.24.jar, groovy-sql-4.0.24.jar, groovy-templates-4.0.24.jar, groovy-typecheckers-4.0.24.jar, groovy-xml-4.0.24.jar.&lt;br /&gt;
Plus four owned transitives (included when Groovy is enabled): asm-analysis-9.7.1.jar, asm-tree-9.7.1.jar, asm-util-9.7.1.jar, javaparser-core-3.26.2.jar&lt;br /&gt;
All 17 JARs above are excluded when Groovy scripting is disabled at install time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Web Admin&#039;&#039;&lt;br /&gt;
* dom4j-2.1.3.jar.  XML utilities.&lt;br /&gt;
* flexmark-0.62.2.jar, flexmark-util-0.62.2.jar. Markdown formatting for Description and Parameter job annotations.&lt;br /&gt;
* jxl-2.6.12.jar. Excel file format utilities. Only required in web administration.&lt;br /&gt;
* opencsv-5.4.jar. Support for CSV downloads in UI.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Web Admin - As of Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Markdown rendering for job and parameter descriptions ships as 12 modular JARs:&lt;br /&gt;
flexmark-0.62.2.jar, flexmark-util-ast-0.62.2.jar, flexmark-util-builder-0.62.2.jar, flexmark-util-collection-0.62.2.jar, flexmark-util-data-0.62.2.jar, flexmark-util-dependency-0.62.2.jar, flexmark-util-format-0.62.2.jar, flexmark-util-html-0.62.2.jar, flexmark-util-misc-0.62.2.jar, flexmark-util-options-0.62.2.jar, flexmark-util-sequence-0.62.2.jar, flexmark-util-visitor-0.62.2.jar&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;, the following changes were made:&lt;br /&gt;
* &#039;&#039;&#039;log4j upgraded&#039;&#039;&#039;: log4j-api and log4j-core updated from 2.17.1 to 2.26.0.&lt;br /&gt;
* &#039;&#039;&#039;Modular Groovy libraries&#039;&#039;&#039;: Obsidian no longer ships &#039;&#039;&#039;apache-groovy-all-4.0.24.jar&#039;&#039;&#039;. Groovy scripting now uses 13 modular JARs (see &#039;&#039;Scripts&#039;&#039; above). When Groovy is disabled at install time, all 13 modules are excluded.&lt;br /&gt;
* * &#039;&#039;&#039;Modular Flexmark libraries&#039;&#039;&#039;: Obsidian no longer ships &#039;&#039;&#039;flexmark-util-0.62.2.jar&#039;&#039;&#039;. Web Admin UI rendering now uses 12 modular Flexmark JARs (see &#039;&#039;Scripts&#039;&#039; above). &lt;br /&gt;
* &#039;&#039;&#039;OAuth / OIDC authentication libraries&#039;&#039;&#039; added (Nimbus OAuth 2.0 SDK and transitive dependencies). Required only when OAuthAuthenticator is configured. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties|Authentication (OAuth/OIDC)]] above.&lt;br /&gt;
* &#039;&#039;&#039;SAML 2.0 authentication libraries&#039;&#039;&#039; added (OpenSAML 4 and its transitive dependencies, plus the SLF4J-to-log4j2 binding). Required only when SAMLAuthenticator is configured. See [[Advanced_Configuration#SAML_Authentication_Properties|Authentication (SAML 2.0)]] above.&lt;br /&gt;
* Embedded Tomcat runtime added (tomcat-embed-core-10.1.39.jar, tomcat-embed-el-10.1.39.jar). Present only when an Embedded Tomcat pack is selected. Replaces the Jetty quick-start distribution which has been removed.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 7.0.0&#039;&#039;&#039;, the following libraries were in use.&lt;br /&gt;
* log4j-api-2.17.1.jar, log4j-core-2.17.1.jar.  Obsidian logging uses log4j2.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 6.0.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* groovy-all-3.0.7.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.4.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* gson-2.8.6.jar. Supports REST API and web administration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.2.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* jakarta.activation-1.2.1.jar. Used for email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.0.3&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* &#039;&#039;Obsidian 5.0.2&#039;&#039; log4j-api-2.16.0.jar, log4j-core-2.16.0.jar.  &lt;br /&gt;
* &#039;&#039;Obsidian 5.0.1&#039;&#039; log4j-api-2.15.0.jar, log4j-core-2.15.0.jar.  &lt;br /&gt;
* &#039;&#039;Obsidian 5.0.0&#039;&#039; log4j-api-2.14.1.jar, log4j-core-2.14.1.jar.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.0.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* activation-1.1.jar, javax.mail-1.5.5.jar.  Used for email notifications.&lt;br /&gt;
* dom4j-1.6.1.jar. XML utilities.&lt;br /&gt;
* gson-2.7.jar. Supports REST API and web administration.&lt;br /&gt;
* bsh-2.0b4.jar.  Support for our Bean Shell script execution. (http://www.beanshell.org).  Only required for BeanShellJob usage.&lt;br /&gt;
* groovy-all-2.4.14.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
* jython-standalone-2.7.0.jar. Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
* jruby-complete-9.2.7.0.jar. Support for Ruby script execution (https://www.jruby.org/). Only required for RubyJob usage.&lt;br /&gt;
* jmustache-1.12.jar. Provides email templating.&lt;br /&gt;
* h2-1.4.192.  H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* log4j-1.2.9.jar.  Obsidian logging uses log4j.&lt;br /&gt;
* mariadb-java-client-1.4.5.jar.  MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* jtds-1.3.1.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc7-12.1.0.1.jar.  Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-9.4.1208.jre7.jar. PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
* opencsv-3.8.jar. Support for CSV downloads in UI.&lt;br /&gt;
* flexmark-0.19.6.jar, flexmark-util-0.19.6.jar (as of 4.3.0). Markdown formatting for Description and Parameter job annotations.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 4.5.1&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* groovy-all-2.4.7.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 4.0.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* javax.mail-1.5.2.jar.  Used for email notifications.&lt;br /&gt;
* jstl.jar, standard.jar.  Web utilities. Only required in web administration.&lt;br /&gt;
* gson-2.2.2.jar. Supports REST API and web administration.&lt;br /&gt;
* groovy-all-2.1.8.jar.  Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
* jython-standalone-2.5.3.jar.  Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
* jmustache-1.8.jar. Provides email templating.&lt;br /&gt;
* h2-1.3.154. H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* mariadb-java-client-1.1.5.jar. MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* jtds-1.2.8.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc6-11.2.0.3.jar. Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-9.0-801.jdbc4.jar.  PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.9.1&#039;&#039;&#039;, the following redundant library was in included in the Obsidian packaging.&lt;br /&gt;
* smtp.jar.  Used for email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.2.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* groovy-all-1.7.6.jar&lt;br /&gt;
* jython.jar (version 2.5.2rc2)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.1.1&#039;&#039;&#039;, the following libraries were also included.&lt;br /&gt;
* carfey-date-1.2.jar or carfey-date-1.1.jar.  Date math/manipulation.&lt;br /&gt;
* jdk-gen.jar, jdk.jar, suite-gen.jar, suite.jar.  Core Obsidian libs.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.1.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* ojdbc14.jar Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 1.5&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* gson-1.5.jar. Supports REST API and web administration.&lt;br /&gt;
&lt;br /&gt;
=== Required Libraries for Embedded API ===&lt;br /&gt;
&lt;br /&gt;
To use the [[Embedded_API|Embedded API]], the following resources should be imported. If you have newer versions of the same JARs in your application, feel free to use the newer version. The JAR files below can generally be found in the &#039;&#039;standalone&#039;&#039; directory if your installation. The &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; file can be extracted from the &amp;lt;code&amp;gt;obsidian-properties-configuration.jar&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;obsidian-yaml-configuration.jar&amp;lt;/code&amp;gt; in the &#039;&#039;standalone&#039;&#039; directory, or from an Obsidian WAR file under &amp;lt;code&amp;gt;WEB-INF/classes&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* com.carfey.properties/com.carfey.yaml (configuration file), or obsidian-properties-configuration.jar/obsidian-yaml-configuration.jar, which contains the configuration file&lt;br /&gt;
* obsidian.jar&lt;br /&gt;
* dom4j-2.1.3.jar&lt;br /&gt;
* flexmark-0.62.2.jar plus the modular Flexmark JARs listed under &#039;&#039;Web Admin&#039;&#039; above&lt;br /&gt;
* gson-2.10.1&lt;br /&gt;
* log4j-api-2.26.0.jar, log4j-core-2.26.0.jar&lt;br /&gt;
* Appropriate JDBC JAR (e.g. mariadb-java-client-2.7.2.jar for MySQL or MariaDB)&lt;br /&gt;
&lt;br /&gt;
In addition, the following resources should be included if you wish to have notification support enabled when using the API:&lt;br /&gt;
* jmustache-1.15.jar&lt;br /&gt;
** jakarta.activation-api-2.1.2.jar, jakarta.mail-api-2.1.2.jar. Used for Jakarta EE email notifications.&lt;br /&gt;
** angus-activation-2.0.1.jar, angus-mail-2.0.2.jar. Optionally used with Jakarta EE email notifications.&lt;br /&gt;
** activation-1.1.jar, javax.mail-1.6.2.jar. Used for javamail (javax) email notifications.&lt;br /&gt;
&lt;br /&gt;
Finally, if you need to configure scripting jobs from the API, you will need to include any applicable JARs from the following list:&lt;br /&gt;
* apache-groovy-all-4.0.24.jar&lt;br /&gt;
* bsh-2.0b5.jar&lt;br /&gt;
* jruby-9.2.17.0.jar&lt;br /&gt;
* jython-standalone-2.7.2.jar&lt;br /&gt;
&lt;br /&gt;
== Disabling Job Execution &amp;amp; Scheduling in the Web Application ==&lt;br /&gt;
See the instructions within the [[Getting_Started#Disabling_Job_Scheduling_in_the_Web_Application|Getting Started Guide]].&lt;br /&gt;
&lt;br /&gt;
== Disabling Automatic Database Updates ==&lt;br /&gt;
See the instructions within the [[Getting_Started#Disabling_Automatic_Database_Updates|Getting Started Guide]].&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Advanced_Configuration&amp;diff=4129</id>
		<title>Advanced Configuration</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Advanced_Configuration&amp;diff=4129"/>
		<updated>2026-07-31T02:09:16Z</updated>

		<summary type="html">&lt;p&gt;Craig: /* Dependent Libraries */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian requires some initial configuration parameters for database connection information and desired authentication mechanisms.  The installer takes care of configuring these as provided.  For your reference should you wish to manually create/edit these, note the details below.&lt;br /&gt;
&lt;br /&gt;
== Configuration Details ==&lt;br /&gt;
&lt;br /&gt;
The following sections show the available properties and sample values that can be used in the &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; properties file. While properties format is shown, all the same configuration items apply to yaml support. See [[#Properties.2FYaml_File|Properties/Yaml File]] for more details on this file.&lt;br /&gt;
&lt;br /&gt;
=== Authentication Properties ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Comment out the native authenticator setup&lt;br /&gt;
#com.carfey.suite.security.Authenticator=com.carfey.suite.security.DBAuthenticator&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LDAP Authentication Properties ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#Set your LDAP info here&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.LdapAuthenticator&lt;br /&gt;
# As of Obsidian 2.1.1, you can specify the attribute type used in building up the distinguished name (dn). If unspecified, defaults to cn&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.dn.attribute=uid&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of dn.bases to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.dn.base=ou=people,o=MyOrgHere&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.url=ldap://localhost:10389&lt;br /&gt;
# As of Obsidian 2.1.1, you can specify the SECURITY_AUTHENTICATION. Defaults to simple. &lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.securityAuthentication=simple&lt;br /&gt;
# Any necessary additional information such as Provider, Principal and Host will need to be handled in your custom class extending LdapAuthenticator.&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 3.5.1, you can login via an LDAP attribute that is not part of the dn. A matching entry is found in the LDAP Directory (using anonymous or a fixed lookup account) &lt;br /&gt;
# after which the dn attribute above is retrieved to perform the user&#039;s authentication. The following 5 attributes are only used for this type of lookup and authentication.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.lookupDnAttribute=false&lt;br /&gt;
# As of Obsidian 3.5.1, this is the attribute name that is being searched for in the LDAP directory to build the dn for eventual authentication.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.loginAttribute=sAMAccountName&lt;br /&gt;
# As of Obsidian 3.5.1, this determines if these lookups will be done anonymously (without a session authenticated by user/password)&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.anonymousEnabled=false&lt;br /&gt;
# As of Obsidian 3.5.1, if anonymous lookups are not permitted or desired, provide the dn and password to be used for lookups. Typically, a read-only account with &lt;br /&gt;
# read rights to the LDAP entries and attributes in question is sufficient.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchUserFullDn=cn=ObsidianLDAPReadOnly,ou=people,o=MyOrgHere&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchPassword=password&lt;br /&gt;
# As of Obsidian 4.5.1, an alternate search base configuration parameter is used for the searchUser&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchUserDnBase=ou=people,o=MyOrgHere&lt;br /&gt;
# As of Obsidian 4.7.1, you may specify one or more additional group membership attributes beyond the defaults&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.groupMembershipAttribute=businessGroup~~~departmentName&lt;br /&gt;
&lt;br /&gt;
# Configure who may access the web app, based on LDAP group membership&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.accessDN=cn=SchedulerAccess,ou=groups,o=MyOrgHere&lt;br /&gt;
&lt;br /&gt;
# Configure the Write role used in the admin web app, based on LDAP group membership (by default, users may only read)&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.write.dn=cn=SchedulerWrite,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.write.roleName=Write&lt;br /&gt;
&lt;br /&gt;
# Configure the Admin role used in the admin web app, based on LDAP group membership (users may configure system parameters, etc).&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.admin.dn=cn=SchedulerAdmin,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.admin.roleName=Admin&lt;br /&gt;
&lt;br /&gt;
# Configure the LimitedRead role used in the admin web app, based on LDAP group membership (by default, users may only read)&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.limitedRead.dn=cn=SchedulerLimitedRead,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.limitedRead.roleName=LimitedRead&lt;br /&gt;
&lt;br /&gt;
# Configure the APIrole used by the REST API, based on LDAP group membership&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.api.dn=cn=SchedulerAPI,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.api.roleName=API&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OAuth / OIDC Authentication Properties ====&lt;br /&gt;
&lt;br /&gt;
To use OAuth / OIDC SSO, set the &#039;&#039;Authenticator&#039;&#039; property to&lt;br /&gt;
&#039;&#039;com.carfey.suite.security.OAuthAuthenticator&#039;&#039; and add the properties below. All&lt;br /&gt;
properties are prefixed with &#039;&#039;com.carfey.suite.security.OAuthAuthenticator.&#039;&#039; (shown&lt;br /&gt;
without prefix in the table for brevity).&lt;br /&gt;
&lt;br /&gt;
===== Required properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;issuer&#039;&#039; || OIDC Issuer URL. Obsidian fetches &#039;&#039;&amp;lt;issuer&amp;gt;/.well-known/openid-configuration&#039;&#039; lazily on first use and caches it.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clientId&#039;&#039; || Client ID registered at the IdP.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clientSecret&#039;&#039; || Client secret. Keep out of version control.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;redirectUri&#039;&#039; || Exact callback URL registered at the IdP (e.g. &#039;&#039;https://app.example.com/oauth/callback&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;scopes&#039;&#039; || Space-separated OAuth scopes (e.g. &#039;&#039;openid profile email groups&#039;&#039;). Must include &#039;&#039;openid&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groups&#039;&#039; || Name of the token claim that carries group memberships (e.g. &#039;&#039;groups&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.group&#039;&#039; || IdP group value for this mapping. &#039;&#039;&amp;lt;idx&amp;gt;&#039;&#039; is an arbitrary stable label (numeric or descriptive).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.roleName&#039;&#039; || Obsidian role for this mapping. At least one &#039;&#039;Admin&#039;&#039; mapping is required. Valid roles: &#039;&#039;&#039;Admin&#039;&#039;&#039;, &#039;&#039;&#039;API&#039;&#039;&#039;, &#039;&#039;&#039;Write&#039;&#039;&#039;, &#039;&#039;&#039;LimitedRead&#039;&#039;&#039;, &#039;&#039;&#039;Operator&#039;&#039;&#039;, &#039;&#039;&#039;Author&#039;&#039;&#039;, and folder-based roles (&#039;&#039;&amp;lt;folder&amp;gt;-(Write|Author|Operator)&#039;&#039;).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Optional properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Default !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;displayName&#039;&#039; || (none) || Label on the SSO button: &amp;quot;Sign in with &#039;&#039;&amp;lt;displayName&amp;gt;&#039;&#039;&amp;quot;. Defaults to &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;logout.mode&#039;&#039; || &#039;&#039;local&#039;&#039; || &#039;&#039;local&#039;&#039;: invalidates Obsidian session only. &#039;&#039;rp&#039;&#039;: RP-initiated logout via IdP &#039;&#039;end_session_endpoint&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;postLogoutRedirectUri&#039;&#039; || (none) || Where to redirect after RP-initiated logout. Ignored when &#039;&#039;logout.mode=local&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.user&#039;&#039; || &#039;&#039;sub&#039;&#039; || Claim used as the internal user ID in audit logs. &#039;&#039;&#039;Strongly recommend keeping as &#039;&#039;sub&#039;&#039;&#039;&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.displayName&#039;&#039; || &#039;&#039;preferred_username&#039;&#039; || Claim shown in the Obsidian UI nav bar. Separate from &#039;&#039;claim.user&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.validateTyp&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, rejects Bearer tokens where &#039;&#039;typ&#039;&#039; is present but not &#039;&#039;at+JWT&#039;&#039;. Set &#039;&#039;false&#039;&#039; only for legacy IdPs that omit &#039;&#039;typ&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clockSkewSeconds&#039;&#039; || &#039;&#039;60&#039;&#039; || Clock skew tolerance for &#039;&#039;exp&#039;&#039;/&#039;&#039;nbf&#039;&#039; validation (seconds).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;metadata.ttlHours&#039;&#039; || &#039;&#039;24&#039;&#039; || OIDC discovery document cache TTL in hours.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;expectedAccessTokenAudience&#039;&#039; || &#039;&#039;clientId&#039;&#039; || Expected &#039;&#039;aud&#039;&#039; for Bearer access tokens. See security note below.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsSource&#039;&#039; || &#039;&#039;idtoken&#039;&#039; || Where to read groups during browser SSO: &#039;&#039;idtoken&#039;&#039;, &#039;&#039;accesstoken&#039;&#039;, or &#039;&#039;userinfo&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsBearerSource&#039;&#039; || &#039;&#039;accesstoken&#039;&#039; || Where to read groups on the Bearer path: &#039;&#039;idtoken&#039;&#039; or &#039;&#039;accesstoken&#039;&#039;. &#039;&#039;&#039;userinfo is not permitted on the Bearer path.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsValuePath&#039;&#039; || (none) || Sub-field name when groups is an array of objects (e.g. &#039;&#039;name&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsStripPrefix&#039;&#039; || (none) || Prefix to strip from each group value (e.g. &#039;&#039;/&#039;&#039; for Keycloak full-path style).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsUserinfoTimeout&#039;&#039; || &#039;&#039;5&#039;&#039; || HTTP timeout in seconds for the userinfo endpoint. Only used when &#039;&#039;claim.groupsSource=userinfo&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.prefix&#039;&#039; || (none) || Prefix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.suffix&#039;&#039;. Map is rebuilt at startup; adding a new root folder requires a restart.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.suffix&#039;&#039; || (none) || Suffix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.prefix&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;requireRole&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, browser SSO fails if IdP groups map to no Obsidian roles. Does not apply to REST Bearer tokens.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Security notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;expectedAccessTokenAudience and cross-JWT confusion&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When &#039;&#039;expectedAccessTokenAudience&#039;&#039; is not set it defaults to &#039;&#039;clientId&#039;&#039;. In many OIDC&lt;br /&gt;
configurations the id_token also carries &#039;&#039;aud=clientId&#039;&#039;, which could allow an id_token&lt;br /&gt;
to be submitted as a Bearer token. The &#039;&#039;claim.validateTyp=true&#039;&#039; default mitigates this&lt;br /&gt;
for compliant IdPs (access tokens carry &#039;&#039;typ=at+JWT&#039;&#039;; id_tokens carry &#039;&#039;typ=JWT&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
For maximum defence, set &#039;&#039;expectedAccessTokenAudience&#039;&#039; to a distinct resource-server&lt;br /&gt;
identifier if your IdP supports it.&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;claim.validateTyp=false&#039;&#039; AND &#039;&#039;expectedAccessTokenAudience&#039;&#039; is not explicitly set,&lt;br /&gt;
Obsidian logs a mandatory startup WARN advising you to set &#039;&#039;expectedAccessTokenAudience&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
===== Keycloak example =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.OAuthAuthenticator&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.issuer=http://localhost:8081/realms/obsidian&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.clientId=obsidian-client&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.clientSecret=test-client-secret-local&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.redirectUri=http://localhost:8080/oauth/callback&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.scopes=openid profile email groups&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.claim.groups=groups&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.claim.groupsSource=idtoken&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.1.group=Obsidian-Admins&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.1.roleName=Admin&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.2.group=Obsidian-Write&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.2.roleName=Write&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.3.group=Obsidian-Read&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.3.roleName=LimitedRead&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.4.group=Obsidian-API&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.4.roleName=API&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.displayName=Keycloak&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.logout.mode=local&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.requireRole=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Provider notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Microsoft Entra ID&#039;&#039;&#039;: groups claim contains GUIDs by default; configure Optional&lt;br /&gt;
Claims for display names. Accounts with 200+ groups trigger overage. Multi-tenant apps not&lt;br /&gt;
supported in v1.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auth0&#039;&#039;&#039;: opaque access tokens (Auth0 default) are not supported on the Bearer path.&lt;br /&gt;
Configure a custom API audience to get JWT access tokens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Okta&#039;&#039;&#039;: use the Custom Authorization Server URL, not the Org Authorization Server.&lt;br /&gt;
&lt;br /&gt;
===== v1 limitations =====&lt;br /&gt;
&lt;br /&gt;
* Opaque access tokens not supported on the REST/Bearer path.&lt;br /&gt;
* No &#039;&#039;jti&#039;&#039; replay protection.&lt;br /&gt;
* Microsoft Entra multi-tenant apps not supported.&lt;br /&gt;
* No post-login IdP session validity check.&lt;br /&gt;
* Dynamic role map requires restart for new root folders.&lt;br /&gt;
* Installer UI supports up to 10 static role pairs; runtime supports unlimited.&lt;br /&gt;
&lt;br /&gt;
==== SAML Authentication Properties ====&lt;br /&gt;
&lt;br /&gt;
To use SAML 2.0 SSO, set the &#039;&#039;Authenticator&#039;&#039; property to&lt;br /&gt;
&#039;&#039;com.carfey.suite.security.saml.SAMLAuthenticator&#039;&#039; and add the properties below. All&lt;br /&gt;
properties are prefixed with &#039;&#039;com.carfey.suite.security.saml.SAMLAuthenticator.&#039;&#039; (shown&lt;br /&gt;
without prefix in the table for brevity). Note the &#039;&#039;.saml.&#039;&#039; package segment in both values.&lt;br /&gt;
&lt;br /&gt;
Obsidian acts as a SAML Service Provider and supports SP-initiated browser login only. See&lt;br /&gt;
[[Authenticator#SAMLAuthenticator_(SAML_2.0_SSO)|Authenticator — SAMLAuthenticator]] for how the&lt;br /&gt;
login and logout flows behave and for the list of &#039;&#039;/saml&#039;&#039; endpoints.&lt;br /&gt;
&lt;br /&gt;
===== Required properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;entityId&#039;&#039; || SP entity ID registered at the IdP (e.g. &#039;&#039;https://app.example.com/obsidian&#039;&#039;). Obsidian also requires this value to appear in the assertion&#039;s &#039;&#039;AudienceRestriction&#039;&#039; when the IdP sends one.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;assertionConsumerServiceUrl&#039;&#039; || Exact ACS URL where the IdP posts responses. This must be Obsidian&#039;s callback endpoint, e.g. &#039;&#039;https://app.example.com/obsidian/saml/callback&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;idpMetadataUrl&#039;&#039; &#039;&#039;&#039;or&#039;&#039;&#039; &#039;&#039;idpMetadataFile&#039;&#039; || Source of the IdP federation metadata: an HTTP(S) URL, or an absolute path to a local XML file. Set exactly one of the two.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups&#039;&#039; || Name of the assertion attribute carrying group memberships (e.g. &#039;&#039;groups&#039;&#039;). Required unless &#039;&#039;dynamicRole.prefix&#039;&#039; is set instead.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.group&#039;&#039; || IdP group value for this mapping. &#039;&#039;&amp;lt;idx&amp;gt;&#039;&#039; is an arbitrary stable label (numeric or descriptive). Not needed if all roles come from &#039;&#039;dynamicRole&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.roleName&#039;&#039; || Obsidian role for this mapping. The installer requires at least one &#039;&#039;Admin&#039;&#039; mapping, and you need one in practice so that somebody can administer Obsidian. Valid roles: &#039;&#039;&#039;Admin&#039;&#039;&#039;, &#039;&#039;&#039;Write&#039;&#039;&#039;, &#039;&#039;&#039;LimitedRead&#039;&#039;&#039;, &#039;&#039;&#039;Operator&#039;&#039;&#039;, &#039;&#039;&#039;Author&#039;&#039;&#039;, and folder-based roles (&#039;&#039;&amp;lt;folder&amp;gt;-(Write|Author|Operator)&#039;&#039;). The &#039;&#039;API&#039;&#039; role has no effect on a SAML install, because the REST API is unavailable.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Because &#039;&#039;signRequests&#039;&#039; defaults to enabled, a default configuration also requires the three&lt;br /&gt;
&#039;&#039;keystore&#039;&#039; properties below. Disable &#039;&#039;signRequests&#039;&#039; only if your IdP does not require signed&lt;br /&gt;
authentication requests.&lt;br /&gt;
&lt;br /&gt;
===== Optional properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Default !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;displayName&#039;&#039; || (none) || Label on the SSO button: &amp;quot;Sign in with &#039;&#039;&amp;lt;displayName&amp;gt;&#039;&#039;&amp;quot;. Defaults to &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;signRequests&#039;&#039; || &#039;&#039;true&#039;&#039; || Signs the outbound &#039;&#039;AuthnRequest&#039;&#039; and SP &#039;&#039;LogoutRequest&#039;&#039;. Requires the three &#039;&#039;keystore&#039;&#039; properties.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.path&#039;&#039; || (none) || Path to the keystore holding the SP signing key. Required when &#039;&#039;signRequests&#039;&#039; is enabled.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.password&#039;&#039; || (none) || Keystore password. Keep out of version control.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.alias&#039;&#039; || (none) || Alias of the SP signing key within the keystore.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;wantAssertionsSigned&#039;&#039; || &#039;&#039;true&#039;&#039; || Requires the IdP to sign assertions, verified against the signing certificate in the IdP metadata.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;singleLogoutServiceUrl&#039;&#039; || (none) || SP single logout URL. When set it is published in Obsidian&#039;s SP metadata and is matched against the &#039;&#039;Destination&#039;&#039; of an incoming &#039;&#039;LogoutRequest&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;logout.mode&#039;&#039; || &#039;&#039;local&#039;&#039; || &#039;&#039;local&#039;&#039;: invalidates the Obsidian session only. &#039;&#039;slo&#039;&#039;: SP-initiated single logout via the IdP&#039;s HTTP-Redirect &#039;&#039;SingleLogoutService&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;postLogoutRedirectUri&#039;&#039; || (none) || Where to redirect after the IdP&#039;s &#039;&#039;LogoutResponse&#039;&#039;. When blank the browser returns to &#039;&#039;/&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;nameIdFormat&#039;&#039; || &#039;&#039;urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified&#039;&#039; || &#039;&#039;NameIDPolicy&#039;&#039; format requested from the IdP. A transient format logs a startup warning, because transient NameIDs cannot serve as stable user IDs.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.user&#039;&#039; || &#039;&#039;nameId&#039;&#039; || Assertion attribute used as the internal user ID in audit logs. &#039;&#039;&#039;Strongly recommend keeping as &#039;&#039;nameId&#039;&#039;&#039;&#039;&#039;; any other value logs a startup warning.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.displayName&#039;&#039; || (none) || Attribute shown in the Obsidian UI nav bar. Separate from &#039;&#039;attribute.user&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.friendlyName&#039;&#039; || (none) || Match the groups attribute on its &#039;&#039;FriendlyName&#039;&#039; instead of its &#039;&#039;Name&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.valuePath&#039;&#039; || (none) || Sub-field name when the groups attribute holds structured values.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.stripPrefix&#039;&#039; || (none) || Prefix to strip from each group value (e.g. &#039;&#039;/&#039;&#039; for Keycloak full-path style).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;metadata.ttlHours&#039;&#039; || &#039;&#039;24&#039;&#039; || IdP federation metadata cache TTL in hours. A failed refresh falls back to the cached copy with a warning.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clockSkewSeconds&#039;&#039; || &#039;&#039;60&#039;&#039; || Clock skew tolerance for &#039;&#039;NotBefore&#039;&#039; / &#039;&#039;NotOnOrAfter&#039;&#039; validation (seconds).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;forceAuthn&#039;&#039; || &#039;&#039;false&#039;&#039; || Sets &#039;&#039;ForceAuthn=&amp;quot;true&amp;quot;&#039;&#039; on the &#039;&#039;AuthnRequest&#039;&#039;, asking the IdP to reauthenticate the user.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;authnContextClassRef&#039;&#039; || (none) || Requests a specific &#039;&#039;RequestedAuthnContext&#039;&#039;. A differing context in the response is logged as a warning, not rejected.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.prefix&#039;&#039; || (none) || Prefix for auto-generated folder-role group mappings. Usable on its own. The map is built at startup, so adding a new root job folder requires a restart.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.suffix&#039;&#039; || (none) || Optional suffix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.prefix&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;requireRole&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, browser SSO fails if the user&#039;s group attributes map to no Obsidian roles.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Security notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Encrypted assertions are not supported&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Obsidian requires signed, unencrypted assertions. If your SP metadata carries no signing key&lt;br /&gt;
material, Obsidian logs a startup warning: IdPs such as AD FS may then choose to encrypt the&lt;br /&gt;
assertion, which this release cannot process. Configure the SP keystore and register the&lt;br /&gt;
generated metadata so the IdP sees a signing &#039;&#039;KeyDescriptor&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Do not disable both signature settings&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Setting &#039;&#039;signRequests&#039;&#039; and &#039;&#039;wantAssertionsSigned&#039;&#039; both to &#039;&#039;false&#039;&#039; is accepted but logs a&lt;br /&gt;
startup warning. In that state assertions are neither signed nor verified, which is not safe for&lt;br /&gt;
production.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SP-initiated flows and replay&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Obsidian issues an &#039;&#039;AuthnRequest&#039;&#039; and tracks its ID in the browser session for five minutes.&lt;br /&gt;
When the IdP&#039;s response carries &#039;&#039;InResponseTo&#039;&#039;, it must match one of those pending IDs and is&lt;br /&gt;
consumed on use, so the same response cannot be replayed. There is no persistent store of&lt;br /&gt;
processed assertion IDs, so keep assertion lifetimes short at the IdP.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Clustered deployments and IdP-initiated logout&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
An IdP-initiated &#039;&#039;LogoutRequest&#039;&#039; can only end the Obsidian session on the node that receives&lt;br /&gt;
it. Use sticky sessions if you rely on single logout across a cluster.&lt;br /&gt;
&lt;br /&gt;
===== Keycloak example =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.saml.SAMLAuthenticator&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.entityId=https://app.example.com/obsidian&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.assertionConsumerServiceUrl=https://app.example.com/obsidian/saml/callback&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.idpMetadataUrl=https://keycloak.example.com/realms/obsidian/protocol/saml/descriptor&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.path=/opt/obsidian/saml-sp.p12&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.password=changeit&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.alias=obsidian-sp&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.attribute.groups=groups&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.1.group=Obsidian-Admins&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.1.roleName=Admin&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.2.group=Obsidian-Write&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.2.roleName=Write&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.3.group=Obsidian-Read&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.3.roleName=LimitedRead&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.displayName=Keycloak&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.logout.mode=local&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.requireRole=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Provider notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Microsoft Entra ID&#039;&#039;&#039;: group claims are omitted from SAML assertions once an account belongs to&lt;br /&gt;
roughly 150 groups. Filter the groups sent to the application, or map app roles instead. Note this&lt;br /&gt;
threshold differs from the OIDC one described above.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AD FS&#039;&#039;&#039;: the federation metadata document is normally at&lt;br /&gt;
&#039;&#039;/FederationMetadata/2007-06/FederationMetadata.xml&#039;&#039;. Register signing-capable SP metadata so AD FS&lt;br /&gt;
does not encrypt the assertion, which Obsidian cannot decrypt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Okta&#039;&#039;&#039;: use the application&#039;s SAML metadata URL for &#039;&#039;idpMetadataUrl&#039;&#039;. Okta&#039;s logout endpoints&lt;br /&gt;
may be POST-only; if no HTTP-Redirect &#039;&#039;SingleLogoutService&#039;&#039; is advertised, &#039;&#039;logout.mode=slo&#039;&#039;&lt;br /&gt;
falls back to local logout.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auth0&#039;&#039;&#039;: federation metadata is served at &#039;&#039;samlp/metadata/&amp;lt;client-id&amp;gt;&#039;&#039;. Group memberships must&lt;br /&gt;
be added to the assertion with an Auth0 Action or rule.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Keycloak and other generic IdPs&#039;&#039;&#039;: Keycloak publishes a SAML descriptor at&lt;br /&gt;
&#039;&#039;/realms/&amp;lt;realm&amp;gt;/protocol/saml/descriptor&#039;&#039;. Any IdP that publishes SAML 2.0 metadata with an&lt;br /&gt;
HTTP-POST &#039;&#039;AssertionConsumerService&#039;&#039; target and a signing certificate will work.&lt;br /&gt;
&lt;br /&gt;
===== v1 limitations =====&lt;br /&gt;
&lt;br /&gt;
* The REST API is unavailable while &#039;&#039;SAMLAuthenticator&#039;&#039; is active — there is no Bearer or JWT equivalent.&lt;br /&gt;
* SP-initiated login only.&lt;br /&gt;
* Encrypted assertions are not supported.&lt;br /&gt;
* No persistent assertion replay store beyond the single-use &#039;&#039;InResponseTo&#039;&#039; match and the assertion time window.&lt;br /&gt;
* Dynamic role map (&#039;&#039;dynamicRole.prefix&#039;&#039;/&#039;&#039;suffix&#039;&#039;) requires a server restart when new root job folders are added.&lt;br /&gt;
* Installer UI supports up to 10 static role pairs; the runtime supports unlimited.&lt;br /&gt;
&lt;br /&gt;
=== SMTP Mail Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Email configuration for notifications, if desired&lt;br /&gt;
#for straight up open relay, just specify the host using&lt;br /&gt;
mail.smtp.host=smtp.myopenrelayhost.com&lt;br /&gt;
mail.smtp.port=port &lt;br /&gt;
#(standard ports are 25, 465 for SSL, 587 for TLS)&lt;br /&gt;
&lt;br /&gt;
#for using TLS and SSL, provide these as necessary&lt;br /&gt;
mail.smtp.socketFactory.port=port&lt;br /&gt;
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory&lt;br /&gt;
mail.smtp.auth=true&lt;br /&gt;
mail.smtp.user=myauthenticatedemailuser@email.com&lt;br /&gt;
mail.smtp.password=PASSWORD&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 2.5.0, we support JNDI lookups for email sessions. All other email properties can be excluded.&lt;br /&gt;
mail.session.jndi.path=java:comp/env/mail/session&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Database Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Database configuration&lt;br /&gt;
com.carfey.obsidian.db.url=jdbc:mysql://localhost/obsidian&lt;br /&gt;
com.carfey.obsidian.db.userId=user&lt;br /&gt;
com.carfey.obsidian.db.password=pass&lt;br /&gt;
&lt;br /&gt;
# Sample JDBC URL formats for all platforms&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:mysql://localhost:3306/obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:h2:C:/dev/workspace/obsidian;MVCC=TRUE&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:oracle:thin:@localhost:1521:obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:postgresql://localhost:5432/obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:sqlserver://localhost:1433;databaseName=obsidian&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 6.3.0, we support constructing simple JDBC urls from component properties&lt;br /&gt;
# Only supports basic URL formats noted above and does not support H2&lt;br /&gt;
#com.carfey.obsidian.db.host=localhost&lt;br /&gt;
#com.carfey.obsidian.db.port=3306&lt;br /&gt;
#com.carfey.obsidian.db.databaseName=obsidianDB&lt;br /&gt;
#com.carfey.obsidian.db.dbType=mariadb/mysql/oracle/postgresql/sqlserver&lt;br /&gt;
#com.carfey.obsidian.db.oracleSid=SIDORCL&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 2.5.0, we support JNDI lookups for database connectivity.&lt;br /&gt;
com.carfey.obsidian.db.url=java:comp/env/jdbc/obsidian&lt;br /&gt;
com.carfey.obsidian.db.jndiType=mysql&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=mysql is also used for MariaDB&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=oracle&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=postgresql&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=h2&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=sqlserver&lt;br /&gt;
&lt;br /&gt;
# Table prefix may be used to add a prefix to tables and related database objects. It must be 6 or fewer characters and can contain letters or underscores. Available as of version 2.0.&lt;br /&gt;
com.carfey.obsidian.db.tablePrefix=OBSDN_&lt;br /&gt;
&lt;br /&gt;
# For Oracle databases, when Obsidian&#039;s tables exist in a different schema from the user specified above, or if the user does not default to the schema matching its name, specify the target schema here. Available as of version 2.1.&lt;br /&gt;
# For details on the required privileges to run with an alternate user, see https://wiki.obsidianscheduler.com/docs/Obsidian_Tables#Oracle_Privileges&lt;br /&gt;
# This same configuration item can be used to configure the Postgresql schema. If unspecified, the default is public. Available as of Obsidian 2.1.1. &lt;br /&gt;
# If using JNDI and Oracle/Postgresql, we recommend you set this value to ensure best performance.&lt;br /&gt;
com.carfey.obsidian.db.schema=obsidian&lt;br /&gt;
&lt;br /&gt;
#default max connections per pool if not specified is 50&lt;br /&gt;
com.carfey.obsidian.db.maxConnections=40&lt;br /&gt;
&lt;br /&gt;
#default millis timeout to retrieve available connection from pool if not specified is 2000&lt;br /&gt;
com.carfey.obsidian.db.connectionTimeout=2000&lt;br /&gt;
&lt;br /&gt;
# As of 3.4.0, you can disable pooling of connections, which is suggested only if using another connection pool through JNDI. When true, maxConnections is ignored. &lt;br /&gt;
# When a JNDI data source is selected in the installer, this is set to true by default.&lt;br /&gt;
com.carfey.obsidian.db.disablePooling=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.3.0, Oracle database supports configured wait time.&lt;br /&gt;
com.carfey.obsidian.db.timedLockWaitEnabled=true (default)&lt;br /&gt;
com.carfey.obsidian.db.timedLockWaitSeconds=1 (default, prior to 4.3.0, indefinite wait)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Logging Properties ===&lt;br /&gt;
As of &#039;&#039;&#039;Obsidian 5.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Please see [https://logging.apache.org/log4j/2.x/manual/configuration.html Log4j2 configuration]&lt;br /&gt;
&lt;br /&gt;
Before &#039;&#039;&#039;Obsidian 5.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Please see [https://logging.apache.org/log4j/1.2/manual.html Log4j Configuration]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Miscellaneous Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# If you wish to change the tokens used to reference global parameters in your job configuration, use the following two values:&lt;br /&gt;
global.param.start.token={{&lt;br /&gt;
global.param.end.token=}}&lt;br /&gt;
&lt;br /&gt;
# Populated by the installer, these values can be used to initialize licence key information into the database.&lt;br /&gt;
com.carfey.obsidian.licence.key=licenceKey&lt;br /&gt;
# Name is only required for hardware-linked or site licenses. Use the name exactly as provided by Carfey Software Corporation in your license email.&lt;br /&gt;
com.carfey.obsidian.licence.name=licenceeName&lt;br /&gt;
&lt;br /&gt;
# As of 2.8.0, if you have issues with classloading (e.g. in Grails or Spring), this flag enables usage of the context classloader .&lt;br /&gt;
com.carfey.jdk.useContextClassLoader=true&lt;br /&gt;
&lt;br /&gt;
# As of 2.9.0, you can set the scheduler host designator name multiple times in the same JVM by using this parameter:&lt;br /&gt;
com.carfey.obsidian.schedulerDesignation=obsidian-dev&lt;br /&gt;
&lt;br /&gt;
# As of 3.0.0, Job Forking can be enabled and configured per node&lt;br /&gt;
com.carfey.obsidian.jvmJobForkingEnabledOnThisNode=true&lt;br /&gt;
# This property is the location of the fork scripts. Obsidian is bundled with obsidianForkedJob.bat and obsidianForkedJob.sh. These are the expected script names.&lt;br /&gt;
com.carfey.obsidian.forkedJobScriptLocation=/Obsidian-3.0.0&lt;br /&gt;
# Use the following optional property if you need to override the default classpath that is built using the contents of the standalone directory.&lt;br /&gt;
# This allows for job forking support in embedded and even webapp deployments. Use the classpath format supported by your operating system.&lt;br /&gt;
#com.carfey.obsidian.forkedJobscriptClasspathOverride=&lt;br /&gt;
# Use the following optional property if you wish to include database configuration parameters as arguments passed to the forking scripts and the target forked Obsidian class.&lt;br /&gt;
# For example, you may need to do this if your configuration is done via System Property overrides or programmmatically in your embedded Obsidian instance.&lt;br /&gt;
#com.carfey.obsidian.jvmJobForkingIncludeDbSysParms=true&lt;br /&gt;
&lt;br /&gt;
# Obsolete as of 4.0.0. As of 3.4.0, the base URL used to resolve links in the Obsidian web app can be overridden. This is typically only required when a load balancer or proxy is used to access Obsidian.&lt;br /&gt;
com.carfey.obsidian.baseHrefUrl=http://myhost:8080/obsidian&lt;br /&gt;
&lt;br /&gt;
# As of 4.3.0, description/parameter formatting is supported. Custom formatter (com.carfey.ops.job.config.formatter.Formatter) supported via this parameter.&lt;br /&gt;
com.carfey.obsidian.formatterClass=com.carfey.ops.job.config.formatter.MarkdownFormatter (default)&lt;br /&gt;
&lt;br /&gt;
# As of 4.4.0, support for running DDL outside Obsidian and applying only the data portion of upgrades. May require incremental upgrades. Contact support for assistance. Enabled with the following:&lt;br /&gt;
com.carfey.obsidian.runner.skipDDL=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.5.0, Slack notifications can be automatically configured for FATAL, ERROR and WARN events by setting this property with a Slack incoming web hook URL. The user name property is optional and defaults to Obsidian Scheduler&lt;br /&gt;
com.carfey.obsidian.slack.webhookUrl=https://hooks.slack.com/services/XXXXXXX/YYYYYYYY/ZZZZZZZZZZ&lt;br /&gt;
com.carfey.obsidian.slack.userName=Obsidian Scheduler (prod)&lt;br /&gt;
&lt;br /&gt;
# As of 4.6.0, Obsidian supports starting a node in paused state. This can also be set as a System property.&lt;br /&gt;
com.carfey.obsidian.schedulerPausedOnStartup=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.10.0, Obsidian supports disabling script engine jobs selectively.&lt;br /&gt;
com.carfey.ops.job.script.BeanShellJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.GroovyJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.RubyJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.PythonJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.JavaScriptJob.enabled=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.0.0, Signal interrupt (supported as of 4.5.1) is disabled by default. Use configuration to enable.&lt;br /&gt;
com.carfey.obsidian.signalHandlerActiveForScheduler=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.0, you can send Obsidian events to a REST endpoint via an out-of-the-box Event Hook. The basicAuthorization property is optional, the sample showing its use for myusername:mypassword. The &amp;quot;Basic &amp;quot; prefix is added by Obsidian.&lt;br /&gt;
com.carfey.obsidian.restfulEventHook.url=https://myendpoint.com:1234/rest/obsidian_events&lt;br /&gt;
com.carfey.obsidian.restfulEventHook.basicAuthorization=bXl1c2VybmFtZTpteXBhc3N3b3Jk&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.1, maintenance jobs are automatically scheduled for new installations. Set the following property to disable this behaviour.&lt;br /&gt;
com.carfey.obsidian.skipMaintenanceJobInitialization=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.1, you can send Obsidian events to standard output and error streams using an out-of-the-box Event Hook. You can enable the default behaviour and auto-register it by adding the following configuration.&lt;br /&gt;
com.carfey.obsidian.standardOutputStreamsEventHook.enabled=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Table Prefixes ===&lt;br /&gt;
As shown in full configuration reference, a table name prefix may be specified to create Obsidian tables with names beginning with a specified string.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Table prefix may be used to add a prefix to tables and related database objects. It must be 6 or fewer characters and can contain letters or underscores. Available as of version 2.0.&lt;br /&gt;
com.carfey.obsidian.db.tablePrefix=OBSDN_&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Properties/Yaml File ==&lt;br /&gt;
&lt;br /&gt;
The above configuration must reside in a properties file named &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; or a yaml file named &amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; found according to the rules of [http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String) ClassLoader.getResource].  If both yaml and properties files are found, yaml is loaded first and properties values will override. Some possibilities include &amp;lt;code&amp;gt;WEB-INF/classes/com.carfey.(properties/yaml)&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;com.carfey.(properties/yaml)&amp;lt;/code&amp;gt; file in a directory which is explicitly added to the classpath, or at the root of a jar file as is done in &amp;lt;code&amp;gt;obsidian-props.jar&amp;lt;/code&amp;gt; for standalone deployments. Prior to Obsidian 3.6, this file had to exist on the classpath, even if only using the override options below to configure Obsidian.&lt;br /&gt;
&lt;br /&gt;
Instead of using a classpath resource for properties, you can specify an external properties file using the Java system property &amp;lt;code&amp;gt;carfey.properties.file&amp;lt;/code&amp;gt;. All expected properties must be specified in either the default &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; file on the classpath or in the override file. Any properties found in both files will use the override file&#039;s values. Usage: &amp;lt;code&amp;gt;-Dcarfey.properties.file=/home/obsidian/obsidian.properties&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Instead of using a classpath resource for yaml, you can specify an external yaml file using the Java system property &amp;lt;code&amp;gt;carfey.yaml.file&amp;lt;/code&amp;gt;. All expected configuration values must be specified in either the default &amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; file on the classpath or in the override file. Any configuration values found in both files will use the override file&#039;s values. Usage: &amp;lt;code&amp;gt;-Dcarfey.yaml.file=/home/obsidian/obsidian.yaml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 2.1, you may also use a programmatic properties override. Simply call &amp;lt;code&amp;gt;com.carfey.jdk.sys.Configurator.setOverride(Properties props)&amp;lt;/code&amp;gt; as early as possible in the application startup.  To quote the javadoc of this method, &#039;&#039;For use as a programmatic properties override. This must be called before any classes are accessed either through invocation or class initialization that may require access to configuration done through properties. Best if done as early as possible in code, perhaps first in an entry point class that does little else and then hands-off to existing entry point.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 4.4, you may specify configuration values as System Properties or Environment Variables. Duplicated values are resolved as follows:&lt;br /&gt;
# System Property&lt;br /&gt;
# Environment Variable - &#039;&#039;&#039;&#039;&#039;overridden by System Property&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
# Properties - &#039;&#039;&#039;&#039;&#039;overridden by Environment Variable and System Property&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Notifications Configuration ==&lt;br /&gt;
&lt;br /&gt;
Sending notifications requires SMTP configuration to be defined in the Obsidian properties file. The properties file reference at the beginning of this page includes SMTP configuration details.&lt;br /&gt;
&lt;br /&gt;
In addition to the properties file, you can configure some Notifications settings to tweak how your emails are sent. These values are configurable under the &#039;&#039;Dispatch&#039;&#039; category of the [[Admin Scheduler Settings|scheduler settings]] screen. Defaults are provided, but you can update them appropriately for your needs.  &lt;br /&gt;
&lt;br /&gt;
=== Notification sender address and DMARC ===&lt;br /&gt;
&lt;br /&gt;
The notificationFromEmail scheduler setting controls the From address used for notification emails. It is available under:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Scheduler Settings → Dispatch → notificationFromEmail&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Set this to an address belonging to your organization and authorized by the SMTP service configured for your Obsidian installation.&lt;br /&gt;
&lt;br /&gt;
Do not use ops-notification@obsidianscheduler.com when sending through your own SMTP infrastructure. Your SMTP server normally cannot authenticate mail for the obsidianscheduler.com domain. This can cause SPF, DKIM, and DMARC failures and may result in notifications being quarantined or rejected.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
scheduler-notifications@example.com&lt;br /&gt;
&lt;br /&gt;
After updating the value, trigger a test notification and confirm successful receipt.&lt;br /&gt;
&lt;br /&gt;
[[File:NotifSettings_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
== Dependent Libraries ==&lt;br /&gt;
&lt;br /&gt;
Obsidian Scheduler requires a number of third party libraries, both for the web administration application and the scheduler itself.  Below is information on these libraries and how they are used.  Unless otherwise noted, they are mandatory. As of Obsidian 2.5.0, our installer supports selective [[Installation_Guide#Configuring_3rd_Party_Library_Conflict_Management|conflict management]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Authentication (OAuth / OIDC)&#039;&#039;&lt;br /&gt;
All libraries in this section are only required when OAuthAuthenticator is active. &#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* oauth2-oidc-sdk-11.23.1.jar, nimbus-jose-jwt-10.0.2.jar. Nimbus OAuth 2.0 SDK with OpenID Connect extensions and JOSE+JWT support.&lt;br /&gt;
* content-type-2.3.jar, lang-tag-1.7.jar. Supporting libraries for the Nimbus SDK.&lt;br /&gt;
* json-smart-2.5.2.jar, accessors-smart-2.5.2.jar. JSON parsing for the Nimbus SDK.&lt;br /&gt;
* jcip-annotations-1.0-1.jar. Concurrency annotations for the Nimbus SDK.&lt;br /&gt;
* asm-9.7.1.jar. Bytecode manipulation library; transitive dependency of the Nimbus SDK.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Authentication (SAML 2.0)&#039;&#039;&lt;br /&gt;
All libraries in this section are only required when SAMLAuthenticator is active; the installer removes them when SAML is not the selected authentication type. &#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* opensaml-core-4.3.2.jar, opensaml-saml-api-4.3.2.jar, opensaml-saml-impl-4.3.2.jar, opensaml-security-api-4.3.2.jar, opensaml-security-impl-4.3.2.jar, opensaml-xmlsec-api-4.3.2.jar, opensaml-xmlsec-impl-4.3.2.jar, opensaml-messaging-api-4.3.2.jar, opensaml-profile-api-4.3.2.jar, opensaml-soap-api-4.3.2.jar, opensaml-soap-impl-4.3.2.jar, opensaml-storage-api-4.3.2.jar. OpenSAML 4 modules implementing SAML 2.0 messages, metadata and signature handling.&lt;br /&gt;
* java-support-8.4.2.jar. Shibboleth utility library required by OpenSAML.&lt;br /&gt;
* xmlsec-2.3.4.jar. Apache Santuario XML Signature implementation.&lt;br /&gt;
* cryptacular-1.2.5.jar, bcprov-jdk18on-1.85.jar, bcpkix-jdk18on-1.85.jar, bcutil-jdk18on-1.85.jar. Cryptographic providers used for assertion signature verification and SP key handling.&lt;br /&gt;
* velocity-engine-core-2.3.jar. Template engine used by OpenSAML&#039;s message encoders.&lt;br /&gt;
* commons-lang3-3.20.0.jar, commons-codec-1.15.jar. Apache Commons utilities required by OpenSAML.&lt;br /&gt;
* httpclient-4.5.14.jar, httpcore-4.4.16.jar. HTTP client used to retrieve IdP federation metadata.&lt;br /&gt;
* guava-32.1.3-jre.jar, failureaccess-1.0.1.jar, listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar. Guava collections and its companion placeholder artifacts, required by OpenSAML.&lt;br /&gt;
* metrics-core-4.2.25.jar. Dropwizard Metrics, required by OpenSAML.&lt;br /&gt;
* slf4j-api-2.0.12.jar, log4j-slf4j2-impl-2.26.0.jar. SLF4J API and its log4j2 binding, so OpenSAML&#039;s diagnostic logging is routed into Obsidian&#039;s log4j2 configuration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Embedded Tomcat (Obsidian Embedded Tomcat JAR packs only)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* tomcat-embed-core-10.1.39.jar, tomcat-embed-el-10.1.39.jar — Apache Tomcat embedded components (Apache License 2.0). Present in &#039;&#039;&#039;embed-tomcat-lib/&#039;&#039;&#039; when an Embedded Tomcat pack is selected.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Common&#039;&#039;&lt;br /&gt;
* obsidian.jar.  Core Obsidian lib.&lt;br /&gt;
* gson-2.10.1. Supports REST API and web administration.&lt;br /&gt;
* jmustache-1.15.jar. Provides email templating.&lt;br /&gt;
* log4j-api-2.26.0.jar, log4j-core-2.26.0.jar.  Obsidian logging uses log4j2.&lt;br /&gt;
* snakeyaml-2.3.jar. Support for YAML configuration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Databases&#039;&#039;&lt;br /&gt;
* h2-1.4.200.jar.  H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* mariadb-java-client-2.7.2.jar. MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* mssql-jdbc-9.2.1.jre11.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc11-21.1.0.0.jar. Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-42.2.19.jar.  PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Mail&#039;&#039; &lt;br /&gt;
* activation-1.1.jar, javax.mail-1.6.2.jar. Used for javamail (javax) email notifications.&lt;br /&gt;
* angus-activation-2.0.1.jar, angus-mail-2.0.2.jar. Optionally used with Jakarta EE email notifications.&lt;br /&gt;
* jakarta.activation-api-2.1.2.jar, jakarta.mail-api-2.1.2.jar. Used for Jakarta EE email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Scripts&#039;&#039;&lt;br /&gt;
* bsh-2.0b5.jar.  Support for our Bean Shell script execution. (http://www.beanshell.org).  Only required for BeanShellJob usage.&lt;br /&gt;
* jruby-9.2.17.0.jar. Support for Ruby script execution (https://www.jruby.org/). Only required for RubyJob usage.&lt;br /&gt;
* jython-standalone-2.7.2.jar. Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Scripts - Prior to Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Groovy scripting shipped as a single uber archive: apache-groovy-all-4.0.24.jar&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Scripts - As of Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Groovy scripting ships as 13 modular JARs: groovy-4.0.24.jar, groovy-astbuilder-4.0.24.jar, groovy-datetime-4.0.24.jar, groovy-dateutil-4.0.24.jar, groovy-json-4.0.24.jar, groovy-jsr223-4.0.24.jar, groovy-macro-4.0.24.jar, groovy-macro-library-4.0.24.jar, groovy-nio-4.0.24.jar, groovy-sql-4.0.24.jar, groovy-templates-4.0.24.jar, groovy-typecheckers-4.0.24.jar, groovy-xml-4.0.24.jar.&lt;br /&gt;
Plus four owned transitives (included when Groovy is enabled): asm-analysis-9.7.1.jar, asm-tree-9.7.1.jar, asm-util-9.7.1.jar, javaparser-core-3.26.2.jar&lt;br /&gt;
All 17 JARs above are excluded when Groovy scripting is disabled at install time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Web Admin&#039;&#039;&lt;br /&gt;
* dom4j-2.1.3.jar.  XML utilities.&lt;br /&gt;
* flexmark-0.62.2.jar, flexmark-util-0.62.2.jar. Markdown formatting for Description and Parameter job annotations.&lt;br /&gt;
* jxl-2.6.12.jar. Excel file format utilities. Only required in web administration.&lt;br /&gt;
* opencsv-5.4.jar. Support for CSV downloads in UI.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Web Admin - As of Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Wiki rendering ships as 12 modular JARs:&lt;br /&gt;
flexmark-0.62.2.jar, flexmark-util-ast-0.62.2.jar, flexmark-util-builder-0.62.2.jar, flexmark-util-collection-0.62.2.jar, flexmark-util-data-0.62.2.jar, flexmark-util-dependency-0.62.2.jar, flexmark-util-format-0.62.2.jar, flexmark-util-html-0.62.2.jar, flexmark-util-misc-0.62.2.jar, flexmark-util-options-0.62.2.jar, flexmark-util-sequence-0.62.2.jar, flexmark-util-visitor-0.62.2.jar&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;, the following changes were made:&lt;br /&gt;
* &#039;&#039;&#039;log4j upgraded&#039;&#039;&#039;: log4j-api and log4j-core updated from 2.17.1 to 2.26.0.&lt;br /&gt;
* &#039;&#039;&#039;Modular Groovy libraries&#039;&#039;&#039;: Obsidian no longer ships &#039;&#039;&#039;apache-groovy-all-4.0.24.jar&#039;&#039;&#039;. Groovy scripting now uses 13 modular JARs (see &#039;&#039;Scripts&#039;&#039; above). When Groovy is disabled at install time, all 13 modules are excluded.&lt;br /&gt;
* * &#039;&#039;&#039;Modular Flexmark libraries&#039;&#039;&#039;: Obsidian no longer ships &#039;&#039;&#039;flexmark-util-0.62.2.jar&#039;&#039;&#039;. Web Admin UI rendering now uses 12 modular Flexmark JARs (see &#039;&#039;Scripts&#039;&#039; above). &lt;br /&gt;
* &#039;&#039;&#039;OAuth / OIDC authentication libraries&#039;&#039;&#039; added (Nimbus OAuth 2.0 SDK and transitive dependencies). Required only when OAuthAuthenticator is configured. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties|Authentication (OAuth/OIDC)]] above.&lt;br /&gt;
* &#039;&#039;&#039;SAML 2.0 authentication libraries&#039;&#039;&#039; added (OpenSAML 4 and its transitive dependencies, plus the SLF4J-to-log4j2 binding). Required only when SAMLAuthenticator is configured. See [[Advanced_Configuration#SAML_Authentication_Properties|Authentication (SAML 2.0)]] above.&lt;br /&gt;
* Embedded Tomcat runtime added (tomcat-embed-core-10.1.39.jar, tomcat-embed-el-10.1.39.jar). Present only when an Embedded Tomcat pack is selected. Replaces the Jetty quick-start distribution which has been removed.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 7.0.0&#039;&#039;&#039;, the following libraries were in use.&lt;br /&gt;
* log4j-api-2.17.1.jar, log4j-core-2.17.1.jar.  Obsidian logging uses log4j2.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 6.0.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* groovy-all-3.0.7.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.4.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* gson-2.8.6.jar. Supports REST API and web administration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.2.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* jakarta.activation-1.2.1.jar. Used for email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.0.3&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* &#039;&#039;Obsidian 5.0.2&#039;&#039; log4j-api-2.16.0.jar, log4j-core-2.16.0.jar.  &lt;br /&gt;
* &#039;&#039;Obsidian 5.0.1&#039;&#039; log4j-api-2.15.0.jar, log4j-core-2.15.0.jar.  &lt;br /&gt;
* &#039;&#039;Obsidian 5.0.0&#039;&#039; log4j-api-2.14.1.jar, log4j-core-2.14.1.jar.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.0.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* activation-1.1.jar, javax.mail-1.5.5.jar.  Used for email notifications.&lt;br /&gt;
* dom4j-1.6.1.jar. XML utilities.&lt;br /&gt;
* gson-2.7.jar. Supports REST API and web administration.&lt;br /&gt;
* bsh-2.0b4.jar.  Support for our Bean Shell script execution. (http://www.beanshell.org).  Only required for BeanShellJob usage.&lt;br /&gt;
* groovy-all-2.4.14.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
* jython-standalone-2.7.0.jar. Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
* jruby-complete-9.2.7.0.jar. Support for Ruby script execution (https://www.jruby.org/). Only required for RubyJob usage.&lt;br /&gt;
* jmustache-1.12.jar. Provides email templating.&lt;br /&gt;
* h2-1.4.192.  H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* log4j-1.2.9.jar.  Obsidian logging uses log4j.&lt;br /&gt;
* mariadb-java-client-1.4.5.jar.  MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* jtds-1.3.1.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc7-12.1.0.1.jar.  Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-9.4.1208.jre7.jar. PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
* opencsv-3.8.jar. Support for CSV downloads in UI.&lt;br /&gt;
* flexmark-0.19.6.jar, flexmark-util-0.19.6.jar (as of 4.3.0). Markdown formatting for Description and Parameter job annotations.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 4.5.1&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* groovy-all-2.4.7.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 4.0.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* javax.mail-1.5.2.jar.  Used for email notifications.&lt;br /&gt;
* jstl.jar, standard.jar.  Web utilities. Only required in web administration.&lt;br /&gt;
* gson-2.2.2.jar. Supports REST API and web administration.&lt;br /&gt;
* groovy-all-2.1.8.jar.  Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
* jython-standalone-2.5.3.jar.  Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
* jmustache-1.8.jar. Provides email templating.&lt;br /&gt;
* h2-1.3.154. H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* mariadb-java-client-1.1.5.jar. MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* jtds-1.2.8.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc6-11.2.0.3.jar. Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-9.0-801.jdbc4.jar.  PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.9.1&#039;&#039;&#039;, the following redundant library was in included in the Obsidian packaging.&lt;br /&gt;
* smtp.jar.  Used for email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.2.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* groovy-all-1.7.6.jar&lt;br /&gt;
* jython.jar (version 2.5.2rc2)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.1.1&#039;&#039;&#039;, the following libraries were also included.&lt;br /&gt;
* carfey-date-1.2.jar or carfey-date-1.1.jar.  Date math/manipulation.&lt;br /&gt;
* jdk-gen.jar, jdk.jar, suite-gen.jar, suite.jar.  Core Obsidian libs.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.1.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* ojdbc14.jar Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 1.5&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* gson-1.5.jar. Supports REST API and web administration.&lt;br /&gt;
&lt;br /&gt;
=== Required Libraries for Embedded API ===&lt;br /&gt;
&lt;br /&gt;
To use the [[Embedded_API|Embedded API]], the following resources should be imported. If you have newer versions of the same JARs in your application, feel free to use the newer version. The JAR files below can generally be found in the &#039;&#039;standalone&#039;&#039; directory if your installation. The &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; file can be extracted from the &amp;lt;code&amp;gt;obsidian-properties-configuration.jar&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;obsidian-yaml-configuration.jar&amp;lt;/code&amp;gt; in the &#039;&#039;standalone&#039;&#039; directory, or from an Obsidian WAR file under &amp;lt;code&amp;gt;WEB-INF/classes&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* com.carfey.properties/com.carfey.yaml (configuration file), or obsidian-properties-configuration.jar/obsidian-yaml-configuration.jar, which contains the configuration file&lt;br /&gt;
* obsidian.jar&lt;br /&gt;
* dom4j-2.1.3.jar&lt;br /&gt;
* flexmark-0.62.2.jar plus the modular Flexmark JARs listed under &#039;&#039;Web Admin&#039;&#039; above&lt;br /&gt;
* gson-2.10.1&lt;br /&gt;
* log4j-api-2.26.0.jar, log4j-core-2.26.0.jar&lt;br /&gt;
* Appropriate JDBC JAR (e.g. mariadb-java-client-2.7.2.jar for MySQL or MariaDB)&lt;br /&gt;
&lt;br /&gt;
In addition, the following resources should be included if you wish to have notification support enabled when using the API:&lt;br /&gt;
* jmustache-1.15.jar&lt;br /&gt;
** jakarta.activation-api-2.1.2.jar, jakarta.mail-api-2.1.2.jar. Used for Jakarta EE email notifications.&lt;br /&gt;
** angus-activation-2.0.1.jar, angus-mail-2.0.2.jar. Optionally used with Jakarta EE email notifications.&lt;br /&gt;
** activation-1.1.jar, javax.mail-1.6.2.jar. Used for javamail (javax) email notifications.&lt;br /&gt;
&lt;br /&gt;
Finally, if you need to configure scripting jobs from the API, you will need to include any applicable JARs from the following list:&lt;br /&gt;
* apache-groovy-all-4.0.24.jar&lt;br /&gt;
* bsh-2.0b5.jar&lt;br /&gt;
* jruby-9.2.17.0.jar&lt;br /&gt;
* jython-standalone-2.7.2.jar&lt;br /&gt;
&lt;br /&gt;
== Disabling Job Execution &amp;amp; Scheduling in the Web Application ==&lt;br /&gt;
See the instructions within the [[Getting_Started#Disabling_Job_Scheduling_in_the_Web_Application|Getting Started Guide]].&lt;br /&gt;
&lt;br /&gt;
== Disabling Automatic Database Updates ==&lt;br /&gt;
See the instructions within the [[Getting_Started#Disabling_Automatic_Database_Updates|Getting Started Guide]].&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
	<entry>
		<id>https://wiki.obsidianscheduler.com/doc/index.php?title=Advanced_Configuration&amp;diff=4128</id>
		<title>Advanced Configuration</title>
		<link rel="alternate" type="text/html" href="https://wiki.obsidianscheduler.com/doc/index.php?title=Advanced_Configuration&amp;diff=4128"/>
		<updated>2026-07-31T02:04:01Z</updated>

		<summary type="html">&lt;p&gt;Craig: /* Dependent Libraries */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Obsidian requires some initial configuration parameters for database connection information and desired authentication mechanisms.  The installer takes care of configuring these as provided.  For your reference should you wish to manually create/edit these, note the details below.&lt;br /&gt;
&lt;br /&gt;
== Configuration Details ==&lt;br /&gt;
&lt;br /&gt;
The following sections show the available properties and sample values that can be used in the &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; properties file. While properties format is shown, all the same configuration items apply to yaml support. See [[#Properties.2FYaml_File|Properties/Yaml File]] for more details on this file.&lt;br /&gt;
&lt;br /&gt;
=== Authentication Properties ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Comment out the native authenticator setup&lt;br /&gt;
#com.carfey.suite.security.Authenticator=com.carfey.suite.security.DBAuthenticator&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LDAP Authentication Properties ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#Set your LDAP info here&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.LdapAuthenticator&lt;br /&gt;
# As of Obsidian 2.1.1, you can specify the attribute type used in building up the distinguished name (dn). If unspecified, defaults to cn&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.dn.attribute=uid&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of dn.bases to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.dn.base=ou=people,o=MyOrgHere&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.url=ldap://localhost:10389&lt;br /&gt;
# As of Obsidian 2.1.1, you can specify the SECURITY_AUTHENTICATION. Defaults to simple. &lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.securityAuthentication=simple&lt;br /&gt;
# Any necessary additional information such as Provider, Principal and Host will need to be handled in your custom class extending LdapAuthenticator.&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 3.5.1, you can login via an LDAP attribute that is not part of the dn. A matching entry is found in the LDAP Directory (using anonymous or a fixed lookup account) &lt;br /&gt;
# after which the dn attribute above is retrieved to perform the user&#039;s authentication. The following 5 attributes are only used for this type of lookup and authentication.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.lookupDnAttribute=false&lt;br /&gt;
# As of Obsidian 3.5.1, this is the attribute name that is being searched for in the LDAP directory to build the dn for eventual authentication.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.loginAttribute=sAMAccountName&lt;br /&gt;
# As of Obsidian 3.5.1, this determines if these lookups will be done anonymously (without a session authenticated by user/password)&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.anonymousEnabled=false&lt;br /&gt;
# As of Obsidian 3.5.1, if anonymous lookups are not permitted or desired, provide the dn and password to be used for lookups. Typically, a read-only account with &lt;br /&gt;
# read rights to the LDAP entries and attributes in question is sufficient.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchUserFullDn=cn=ObsidianLDAPReadOnly,ou=people,o=MyOrgHere&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchPassword=password&lt;br /&gt;
# As of Obsidian 4.5.1, an alternate search base configuration parameter is used for the searchUser&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.searchUserDnBase=ou=people,o=MyOrgHere&lt;br /&gt;
# As of Obsidian 4.7.1, you may specify one or more additional group membership attributes beyond the defaults&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.groupMembershipAttribute=businessGroup~~~departmentName&lt;br /&gt;
&lt;br /&gt;
# Configure who may access the web app, based on LDAP group membership&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.accessDN=cn=SchedulerAccess,ou=groups,o=MyOrgHere&lt;br /&gt;
&lt;br /&gt;
# Configure the Write role used in the admin web app, based on LDAP group membership (by default, users may only read)&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.write.dn=cn=SchedulerWrite,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.write.roleName=Write&lt;br /&gt;
&lt;br /&gt;
# Configure the Admin role used in the admin web app, based on LDAP group membership (users may configure system parameters, etc).&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.admin.dn=cn=SchedulerAdmin,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.admin.roleName=Admin&lt;br /&gt;
&lt;br /&gt;
# Configure the LimitedRead role used in the admin web app, based on LDAP group membership (by default, users may only read)&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.limitedRead.dn=cn=SchedulerLimitedRead,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.limitedRead.roleName=LimitedRead&lt;br /&gt;
&lt;br /&gt;
# Configure the APIrole used by the REST API, based on LDAP group membership&lt;br /&gt;
# As of Obsidian 4.5.0, you can provide a delimited list (delimiter is 3 tildes ~~~) of groups to search.&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.api.dn=cn=SchedulerAPI,ou=groups,o=MyOrgHere&lt;br /&gt;
# No need to alter this&lt;br /&gt;
com.carfey.suite.security.LdapAuthenticator.role.api.roleName=API&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== OAuth / OIDC Authentication Properties ====&lt;br /&gt;
&lt;br /&gt;
To use OAuth / OIDC SSO, set the &#039;&#039;Authenticator&#039;&#039; property to&lt;br /&gt;
&#039;&#039;com.carfey.suite.security.OAuthAuthenticator&#039;&#039; and add the properties below. All&lt;br /&gt;
properties are prefixed with &#039;&#039;com.carfey.suite.security.OAuthAuthenticator.&#039;&#039; (shown&lt;br /&gt;
without prefix in the table for brevity).&lt;br /&gt;
&lt;br /&gt;
===== Required properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;issuer&#039;&#039; || OIDC Issuer URL. Obsidian fetches &#039;&#039;&amp;lt;issuer&amp;gt;/.well-known/openid-configuration&#039;&#039; lazily on first use and caches it.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clientId&#039;&#039; || Client ID registered at the IdP.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clientSecret&#039;&#039; || Client secret. Keep out of version control.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;redirectUri&#039;&#039; || Exact callback URL registered at the IdP (e.g. &#039;&#039;https://app.example.com/oauth/callback&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;scopes&#039;&#039; || Space-separated OAuth scopes (e.g. &#039;&#039;openid profile email groups&#039;&#039;). Must include &#039;&#039;openid&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groups&#039;&#039; || Name of the token claim that carries group memberships (e.g. &#039;&#039;groups&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.group&#039;&#039; || IdP group value for this mapping. &#039;&#039;&amp;lt;idx&amp;gt;&#039;&#039; is an arbitrary stable label (numeric or descriptive).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.roleName&#039;&#039; || Obsidian role for this mapping. At least one &#039;&#039;Admin&#039;&#039; mapping is required. Valid roles: &#039;&#039;&#039;Admin&#039;&#039;&#039;, &#039;&#039;&#039;API&#039;&#039;&#039;, &#039;&#039;&#039;Write&#039;&#039;&#039;, &#039;&#039;&#039;LimitedRead&#039;&#039;&#039;, &#039;&#039;&#039;Operator&#039;&#039;&#039;, &#039;&#039;&#039;Author&#039;&#039;&#039;, and folder-based roles (&#039;&#039;&amp;lt;folder&amp;gt;-(Write|Author|Operator)&#039;&#039;).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Optional properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Default !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;displayName&#039;&#039; || (none) || Label on the SSO button: &amp;quot;Sign in with &#039;&#039;&amp;lt;displayName&amp;gt;&#039;&#039;&amp;quot;. Defaults to &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;logout.mode&#039;&#039; || &#039;&#039;local&#039;&#039; || &#039;&#039;local&#039;&#039;: invalidates Obsidian session only. &#039;&#039;rp&#039;&#039;: RP-initiated logout via IdP &#039;&#039;end_session_endpoint&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;postLogoutRedirectUri&#039;&#039; || (none) || Where to redirect after RP-initiated logout. Ignored when &#039;&#039;logout.mode=local&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.user&#039;&#039; || &#039;&#039;sub&#039;&#039; || Claim used as the internal user ID in audit logs. &#039;&#039;&#039;Strongly recommend keeping as &#039;&#039;sub&#039;&#039;&#039;&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.displayName&#039;&#039; || &#039;&#039;preferred_username&#039;&#039; || Claim shown in the Obsidian UI nav bar. Separate from &#039;&#039;claim.user&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.validateTyp&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, rejects Bearer tokens where &#039;&#039;typ&#039;&#039; is present but not &#039;&#039;at+JWT&#039;&#039;. Set &#039;&#039;false&#039;&#039; only for legacy IdPs that omit &#039;&#039;typ&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clockSkewSeconds&#039;&#039; || &#039;&#039;60&#039;&#039; || Clock skew tolerance for &#039;&#039;exp&#039;&#039;/&#039;&#039;nbf&#039;&#039; validation (seconds).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;metadata.ttlHours&#039;&#039; || &#039;&#039;24&#039;&#039; || OIDC discovery document cache TTL in hours.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;expectedAccessTokenAudience&#039;&#039; || &#039;&#039;clientId&#039;&#039; || Expected &#039;&#039;aud&#039;&#039; for Bearer access tokens. See security note below.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsSource&#039;&#039; || &#039;&#039;idtoken&#039;&#039; || Where to read groups during browser SSO: &#039;&#039;idtoken&#039;&#039;, &#039;&#039;accesstoken&#039;&#039;, or &#039;&#039;userinfo&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsBearerSource&#039;&#039; || &#039;&#039;accesstoken&#039;&#039; || Where to read groups on the Bearer path: &#039;&#039;idtoken&#039;&#039; or &#039;&#039;accesstoken&#039;&#039;. &#039;&#039;&#039;userinfo is not permitted on the Bearer path.&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsValuePath&#039;&#039; || (none) || Sub-field name when groups is an array of objects (e.g. &#039;&#039;name&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsStripPrefix&#039;&#039; || (none) || Prefix to strip from each group value (e.g. &#039;&#039;/&#039;&#039; for Keycloak full-path style).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;claim.groupsUserinfoTimeout&#039;&#039; || &#039;&#039;5&#039;&#039; || HTTP timeout in seconds for the userinfo endpoint. Only used when &#039;&#039;claim.groupsSource=userinfo&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.prefix&#039;&#039; || (none) || Prefix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.suffix&#039;&#039;. Map is rebuilt at startup; adding a new root folder requires a restart.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.suffix&#039;&#039; || (none) || Suffix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.prefix&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;requireRole&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, browser SSO fails if IdP groups map to no Obsidian roles. Does not apply to REST Bearer tokens.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Security notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;expectedAccessTokenAudience and cross-JWT confusion&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When &#039;&#039;expectedAccessTokenAudience&#039;&#039; is not set it defaults to &#039;&#039;clientId&#039;&#039;. In many OIDC&lt;br /&gt;
configurations the id_token also carries &#039;&#039;aud=clientId&#039;&#039;, which could allow an id_token&lt;br /&gt;
to be submitted as a Bearer token. The &#039;&#039;claim.validateTyp=true&#039;&#039; default mitigates this&lt;br /&gt;
for compliant IdPs (access tokens carry &#039;&#039;typ=at+JWT&#039;&#039;; id_tokens carry &#039;&#039;typ=JWT&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
For maximum defence, set &#039;&#039;expectedAccessTokenAudience&#039;&#039; to a distinct resource-server&lt;br /&gt;
identifier if your IdP supports it.&lt;br /&gt;
&lt;br /&gt;
If &#039;&#039;claim.validateTyp=false&#039;&#039; AND &#039;&#039;expectedAccessTokenAudience&#039;&#039; is not explicitly set,&lt;br /&gt;
Obsidian logs a mandatory startup WARN advising you to set &#039;&#039;expectedAccessTokenAudience&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
===== Keycloak example =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.OAuthAuthenticator&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.issuer=http://localhost:8081/realms/obsidian&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.clientId=obsidian-client&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.clientSecret=test-client-secret-local&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.redirectUri=http://localhost:8080/oauth/callback&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.scopes=openid profile email groups&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.claim.groups=groups&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.claim.groupsSource=idtoken&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.1.group=Obsidian-Admins&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.1.roleName=Admin&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.2.group=Obsidian-Write&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.2.roleName=Write&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.3.group=Obsidian-Read&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.3.roleName=LimitedRead&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.4.group=Obsidian-API&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.role.4.roleName=API&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.displayName=Keycloak&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.logout.mode=local&lt;br /&gt;
com.carfey.suite.security.OAuthAuthenticator.requireRole=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Provider notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Microsoft Entra ID&#039;&#039;&#039;: groups claim contains GUIDs by default; configure Optional&lt;br /&gt;
Claims for display names. Accounts with 200+ groups trigger overage. Multi-tenant apps not&lt;br /&gt;
supported in v1.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auth0&#039;&#039;&#039;: opaque access tokens (Auth0 default) are not supported on the Bearer path.&lt;br /&gt;
Configure a custom API audience to get JWT access tokens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Okta&#039;&#039;&#039;: use the Custom Authorization Server URL, not the Org Authorization Server.&lt;br /&gt;
&lt;br /&gt;
===== v1 limitations =====&lt;br /&gt;
&lt;br /&gt;
* Opaque access tokens not supported on the REST/Bearer path.&lt;br /&gt;
* No &#039;&#039;jti&#039;&#039; replay protection.&lt;br /&gt;
* Microsoft Entra multi-tenant apps not supported.&lt;br /&gt;
* No post-login IdP session validity check.&lt;br /&gt;
* Dynamic role map requires restart for new root folders.&lt;br /&gt;
* Installer UI supports up to 10 static role pairs; runtime supports unlimited.&lt;br /&gt;
&lt;br /&gt;
==== SAML Authentication Properties ====&lt;br /&gt;
&lt;br /&gt;
To use SAML 2.0 SSO, set the &#039;&#039;Authenticator&#039;&#039; property to&lt;br /&gt;
&#039;&#039;com.carfey.suite.security.saml.SAMLAuthenticator&#039;&#039; and add the properties below. All&lt;br /&gt;
properties are prefixed with &#039;&#039;com.carfey.suite.security.saml.SAMLAuthenticator.&#039;&#039; (shown&lt;br /&gt;
without prefix in the table for brevity). Note the &#039;&#039;.saml.&#039;&#039; package segment in both values.&lt;br /&gt;
&lt;br /&gt;
Obsidian acts as a SAML Service Provider and supports SP-initiated browser login only. See&lt;br /&gt;
[[Authenticator#SAMLAuthenticator_(SAML_2.0_SSO)|Authenticator — SAMLAuthenticator]] for how the&lt;br /&gt;
login and logout flows behave and for the list of &#039;&#039;/saml&#039;&#039; endpoints.&lt;br /&gt;
&lt;br /&gt;
===== Required properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;entityId&#039;&#039; || SP entity ID registered at the IdP (e.g. &#039;&#039;https://app.example.com/obsidian&#039;&#039;). Obsidian also requires this value to appear in the assertion&#039;s &#039;&#039;AudienceRestriction&#039;&#039; when the IdP sends one.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;assertionConsumerServiceUrl&#039;&#039; || Exact ACS URL where the IdP posts responses. This must be Obsidian&#039;s callback endpoint, e.g. &#039;&#039;https://app.example.com/obsidian/saml/callback&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;idpMetadataUrl&#039;&#039; &#039;&#039;&#039;or&#039;&#039;&#039; &#039;&#039;idpMetadataFile&#039;&#039; || Source of the IdP federation metadata: an HTTP(S) URL, or an absolute path to a local XML file. Set exactly one of the two.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups&#039;&#039; || Name of the assertion attribute carrying group memberships (e.g. &#039;&#039;groups&#039;&#039;). Required unless &#039;&#039;dynamicRole.prefix&#039;&#039; is set instead.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.group&#039;&#039; || IdP group value for this mapping. &#039;&#039;&amp;lt;idx&amp;gt;&#039;&#039; is an arbitrary stable label (numeric or descriptive). Not needed if all roles come from &#039;&#039;dynamicRole&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;role.&amp;lt;idx&amp;gt;.roleName&#039;&#039; || Obsidian role for this mapping. The installer requires at least one &#039;&#039;Admin&#039;&#039; mapping, and you need one in practice so that somebody can administer Obsidian. Valid roles: &#039;&#039;&#039;Admin&#039;&#039;&#039;, &#039;&#039;&#039;Write&#039;&#039;&#039;, &#039;&#039;&#039;LimitedRead&#039;&#039;&#039;, &#039;&#039;&#039;Operator&#039;&#039;&#039;, &#039;&#039;&#039;Author&#039;&#039;&#039;, and folder-based roles (&#039;&#039;&amp;lt;folder&amp;gt;-(Write|Author|Operator)&#039;&#039;). The &#039;&#039;API&#039;&#039; role has no effect on a SAML install, because the REST API is unavailable.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Because &#039;&#039;signRequests&#039;&#039; defaults to enabled, a default configuration also requires the three&lt;br /&gt;
&#039;&#039;keystore&#039;&#039; properties below. Disable &#039;&#039;signRequests&#039;&#039; only if your IdP does not require signed&lt;br /&gt;
authentication requests.&lt;br /&gt;
&lt;br /&gt;
===== Optional properties =====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Property !! Default !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;displayName&#039;&#039; || (none) || Label on the SSO button: &amp;quot;Sign in with &#039;&#039;&amp;lt;displayName&amp;gt;&#039;&#039;&amp;quot;. Defaults to &amp;quot;Sign in with SSO&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;signRequests&#039;&#039; || &#039;&#039;true&#039;&#039; || Signs the outbound &#039;&#039;AuthnRequest&#039;&#039; and SP &#039;&#039;LogoutRequest&#039;&#039;. Requires the three &#039;&#039;keystore&#039;&#039; properties.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.path&#039;&#039; || (none) || Path to the keystore holding the SP signing key. Required when &#039;&#039;signRequests&#039;&#039; is enabled.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.password&#039;&#039; || (none) || Keystore password. Keep out of version control.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;keystore.alias&#039;&#039; || (none) || Alias of the SP signing key within the keystore.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;wantAssertionsSigned&#039;&#039; || &#039;&#039;true&#039;&#039; || Requires the IdP to sign assertions, verified against the signing certificate in the IdP metadata.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;singleLogoutServiceUrl&#039;&#039; || (none) || SP single logout URL. When set it is published in Obsidian&#039;s SP metadata and is matched against the &#039;&#039;Destination&#039;&#039; of an incoming &#039;&#039;LogoutRequest&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;logout.mode&#039;&#039; || &#039;&#039;local&#039;&#039; || &#039;&#039;local&#039;&#039;: invalidates the Obsidian session only. &#039;&#039;slo&#039;&#039;: SP-initiated single logout via the IdP&#039;s HTTP-Redirect &#039;&#039;SingleLogoutService&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;postLogoutRedirectUri&#039;&#039; || (none) || Where to redirect after the IdP&#039;s &#039;&#039;LogoutResponse&#039;&#039;. When blank the browser returns to &#039;&#039;/&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;nameIdFormat&#039;&#039; || &#039;&#039;urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified&#039;&#039; || &#039;&#039;NameIDPolicy&#039;&#039; format requested from the IdP. A transient format logs a startup warning, because transient NameIDs cannot serve as stable user IDs.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.user&#039;&#039; || &#039;&#039;nameId&#039;&#039; || Assertion attribute used as the internal user ID in audit logs. &#039;&#039;&#039;Strongly recommend keeping as &#039;&#039;nameId&#039;&#039;&#039;&#039;&#039;; any other value logs a startup warning.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.displayName&#039;&#039; || (none) || Attribute shown in the Obsidian UI nav bar. Separate from &#039;&#039;attribute.user&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.friendlyName&#039;&#039; || (none) || Match the groups attribute on its &#039;&#039;FriendlyName&#039;&#039; instead of its &#039;&#039;Name&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.valuePath&#039;&#039; || (none) || Sub-field name when the groups attribute holds structured values.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;attribute.groups.stripPrefix&#039;&#039; || (none) || Prefix to strip from each group value (e.g. &#039;&#039;/&#039;&#039; for Keycloak full-path style).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;metadata.ttlHours&#039;&#039; || &#039;&#039;24&#039;&#039; || IdP federation metadata cache TTL in hours. A failed refresh falls back to the cached copy with a warning.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;clockSkewSeconds&#039;&#039; || &#039;&#039;60&#039;&#039; || Clock skew tolerance for &#039;&#039;NotBefore&#039;&#039; / &#039;&#039;NotOnOrAfter&#039;&#039; validation (seconds).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;forceAuthn&#039;&#039; || &#039;&#039;false&#039;&#039; || Sets &#039;&#039;ForceAuthn=&amp;quot;true&amp;quot;&#039;&#039; on the &#039;&#039;AuthnRequest&#039;&#039;, asking the IdP to reauthenticate the user.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;authnContextClassRef&#039;&#039; || (none) || Requests a specific &#039;&#039;RequestedAuthnContext&#039;&#039;. A differing context in the response is logged as a warning, not rejected.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.prefix&#039;&#039; || (none) || Prefix for auto-generated folder-role group mappings. Usable on its own. The map is built at startup, so adding a new root job folder requires a restart.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;dynamicRole.suffix&#039;&#039; || (none) || Optional suffix for auto-generated folder-role group mappings. Requires &#039;&#039;dynamicRole.prefix&#039;&#039;.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;requireRole&#039;&#039; || &#039;&#039;true&#039;&#039; || When &#039;&#039;true&#039;&#039;, browser SSO fails if the user&#039;s group attributes map to no Obsidian roles.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== Security notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Encrypted assertions are not supported&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Obsidian requires signed, unencrypted assertions. If your SP metadata carries no signing key&lt;br /&gt;
material, Obsidian logs a startup warning: IdPs such as AD FS may then choose to encrypt the&lt;br /&gt;
assertion, which this release cannot process. Configure the SP keystore and register the&lt;br /&gt;
generated metadata so the IdP sees a signing &#039;&#039;KeyDescriptor&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Do not disable both signature settings&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Setting &#039;&#039;signRequests&#039;&#039; and &#039;&#039;wantAssertionsSigned&#039;&#039; both to &#039;&#039;false&#039;&#039; is accepted but logs a&lt;br /&gt;
startup warning. In that state assertions are neither signed nor verified, which is not safe for&lt;br /&gt;
production.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SP-initiated flows and replay&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Obsidian issues an &#039;&#039;AuthnRequest&#039;&#039; and tracks its ID in the browser session for five minutes.&lt;br /&gt;
When the IdP&#039;s response carries &#039;&#039;InResponseTo&#039;&#039;, it must match one of those pending IDs and is&lt;br /&gt;
consumed on use, so the same response cannot be replayed. There is no persistent store of&lt;br /&gt;
processed assertion IDs, so keep assertion lifetimes short at the IdP.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Clustered deployments and IdP-initiated logout&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
An IdP-initiated &#039;&#039;LogoutRequest&#039;&#039; can only end the Obsidian session on the node that receives&lt;br /&gt;
it. Use sticky sessions if you rely on single logout across a cluster.&lt;br /&gt;
&lt;br /&gt;
===== Keycloak example =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
com.carfey.suite.security.Authenticator=com.carfey.suite.security.saml.SAMLAuthenticator&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.entityId=https://app.example.com/obsidian&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.assertionConsumerServiceUrl=https://app.example.com/obsidian/saml/callback&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.idpMetadataUrl=https://keycloak.example.com/realms/obsidian/protocol/saml/descriptor&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.path=/opt/obsidian/saml-sp.p12&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.password=changeit&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.keystore.alias=obsidian-sp&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.attribute.groups=groups&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.1.group=Obsidian-Admins&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.1.roleName=Admin&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.2.group=Obsidian-Write&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.2.roleName=Write&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.3.group=Obsidian-Read&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.role.3.roleName=LimitedRead&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.displayName=Keycloak&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.logout.mode=local&lt;br /&gt;
com.carfey.suite.security.saml.SAMLAuthenticator.requireRole=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Provider notes =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Microsoft Entra ID&#039;&#039;&#039;: group claims are omitted from SAML assertions once an account belongs to&lt;br /&gt;
roughly 150 groups. Filter the groups sent to the application, or map app roles instead. Note this&lt;br /&gt;
threshold differs from the OIDC one described above.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AD FS&#039;&#039;&#039;: the federation metadata document is normally at&lt;br /&gt;
&#039;&#039;/FederationMetadata/2007-06/FederationMetadata.xml&#039;&#039;. Register signing-capable SP metadata so AD FS&lt;br /&gt;
does not encrypt the assertion, which Obsidian cannot decrypt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Okta&#039;&#039;&#039;: use the application&#039;s SAML metadata URL for &#039;&#039;idpMetadataUrl&#039;&#039;. Okta&#039;s logout endpoints&lt;br /&gt;
may be POST-only; if no HTTP-Redirect &#039;&#039;SingleLogoutService&#039;&#039; is advertised, &#039;&#039;logout.mode=slo&#039;&#039;&lt;br /&gt;
falls back to local logout.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auth0&#039;&#039;&#039;: federation metadata is served at &#039;&#039;samlp/metadata/&amp;lt;client-id&amp;gt;&#039;&#039;. Group memberships must&lt;br /&gt;
be added to the assertion with an Auth0 Action or rule.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Keycloak and other generic IdPs&#039;&#039;&#039;: Keycloak publishes a SAML descriptor at&lt;br /&gt;
&#039;&#039;/realms/&amp;lt;realm&amp;gt;/protocol/saml/descriptor&#039;&#039;. Any IdP that publishes SAML 2.0 metadata with an&lt;br /&gt;
HTTP-POST &#039;&#039;AssertionConsumerService&#039;&#039; target and a signing certificate will work.&lt;br /&gt;
&lt;br /&gt;
===== v1 limitations =====&lt;br /&gt;
&lt;br /&gt;
* The REST API is unavailable while &#039;&#039;SAMLAuthenticator&#039;&#039; is active — there is no Bearer or JWT equivalent.&lt;br /&gt;
* SP-initiated login only.&lt;br /&gt;
* Encrypted assertions are not supported.&lt;br /&gt;
* No persistent assertion replay store beyond the single-use &#039;&#039;InResponseTo&#039;&#039; match and the assertion time window.&lt;br /&gt;
* Dynamic role map (&#039;&#039;dynamicRole.prefix&#039;&#039;/&#039;&#039;suffix&#039;&#039;) requires a server restart when new root job folders are added.&lt;br /&gt;
* Installer UI supports up to 10 static role pairs; the runtime supports unlimited.&lt;br /&gt;
&lt;br /&gt;
=== SMTP Mail Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Email configuration for notifications, if desired&lt;br /&gt;
#for straight up open relay, just specify the host using&lt;br /&gt;
mail.smtp.host=smtp.myopenrelayhost.com&lt;br /&gt;
mail.smtp.port=port &lt;br /&gt;
#(standard ports are 25, 465 for SSL, 587 for TLS)&lt;br /&gt;
&lt;br /&gt;
#for using TLS and SSL, provide these as necessary&lt;br /&gt;
mail.smtp.socketFactory.port=port&lt;br /&gt;
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory&lt;br /&gt;
mail.smtp.auth=true&lt;br /&gt;
mail.smtp.user=myauthenticatedemailuser@email.com&lt;br /&gt;
mail.smtp.password=PASSWORD&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 2.5.0, we support JNDI lookups for email sessions. All other email properties can be excluded.&lt;br /&gt;
mail.session.jndi.path=java:comp/env/mail/session&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Database Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Database configuration&lt;br /&gt;
com.carfey.obsidian.db.url=jdbc:mysql://localhost/obsidian&lt;br /&gt;
com.carfey.obsidian.db.userId=user&lt;br /&gt;
com.carfey.obsidian.db.password=pass&lt;br /&gt;
&lt;br /&gt;
# Sample JDBC URL formats for all platforms&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:mysql://localhost:3306/obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:h2:C:/dev/workspace/obsidian;MVCC=TRUE&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:oracle:thin:@localhost:1521:obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:postgresql://localhost:5432/obsidian&lt;br /&gt;
#com.carfey.obsidian.db.url=jdbc:sqlserver://localhost:1433;databaseName=obsidian&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 6.3.0, we support constructing simple JDBC urls from component properties&lt;br /&gt;
# Only supports basic URL formats noted above and does not support H2&lt;br /&gt;
#com.carfey.obsidian.db.host=localhost&lt;br /&gt;
#com.carfey.obsidian.db.port=3306&lt;br /&gt;
#com.carfey.obsidian.db.databaseName=obsidianDB&lt;br /&gt;
#com.carfey.obsidian.db.dbType=mariadb/mysql/oracle/postgresql/sqlserver&lt;br /&gt;
#com.carfey.obsidian.db.oracleSid=SIDORCL&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
# As of Obsidian 2.5.0, we support JNDI lookups for database connectivity.&lt;br /&gt;
com.carfey.obsidian.db.url=java:comp/env/jdbc/obsidian&lt;br /&gt;
com.carfey.obsidian.db.jndiType=mysql&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=mysql is also used for MariaDB&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=oracle&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=postgresql&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=h2&lt;br /&gt;
#com.carfey.obsidian.db.jndiType=sqlserver&lt;br /&gt;
&lt;br /&gt;
# Table prefix may be used to add a prefix to tables and related database objects. It must be 6 or fewer characters and can contain letters or underscores. Available as of version 2.0.&lt;br /&gt;
com.carfey.obsidian.db.tablePrefix=OBSDN_&lt;br /&gt;
&lt;br /&gt;
# For Oracle databases, when Obsidian&#039;s tables exist in a different schema from the user specified above, or if the user does not default to the schema matching its name, specify the target schema here. Available as of version 2.1.&lt;br /&gt;
# For details on the required privileges to run with an alternate user, see https://wiki.obsidianscheduler.com/docs/Obsidian_Tables#Oracle_Privileges&lt;br /&gt;
# This same configuration item can be used to configure the Postgresql schema. If unspecified, the default is public. Available as of Obsidian 2.1.1. &lt;br /&gt;
# If using JNDI and Oracle/Postgresql, we recommend you set this value to ensure best performance.&lt;br /&gt;
com.carfey.obsidian.db.schema=obsidian&lt;br /&gt;
&lt;br /&gt;
#default max connections per pool if not specified is 50&lt;br /&gt;
com.carfey.obsidian.db.maxConnections=40&lt;br /&gt;
&lt;br /&gt;
#default millis timeout to retrieve available connection from pool if not specified is 2000&lt;br /&gt;
com.carfey.obsidian.db.connectionTimeout=2000&lt;br /&gt;
&lt;br /&gt;
# As of 3.4.0, you can disable pooling of connections, which is suggested only if using another connection pool through JNDI. When true, maxConnections is ignored. &lt;br /&gt;
# When a JNDI data source is selected in the installer, this is set to true by default.&lt;br /&gt;
com.carfey.obsidian.db.disablePooling=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.3.0, Oracle database supports configured wait time.&lt;br /&gt;
com.carfey.obsidian.db.timedLockWaitEnabled=true (default)&lt;br /&gt;
com.carfey.obsidian.db.timedLockWaitSeconds=1 (default, prior to 4.3.0, indefinite wait)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Logging Properties ===&lt;br /&gt;
As of &#039;&#039;&#039;Obsidian 5.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Please see [https://logging.apache.org/log4j/2.x/manual/configuration.html Log4j2 configuration]&lt;br /&gt;
&lt;br /&gt;
Before &#039;&#039;&#039;Obsidian 5.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Please see [https://logging.apache.org/log4j/1.2/manual.html Log4j Configuration]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Miscellaneous Properties ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# If you wish to change the tokens used to reference global parameters in your job configuration, use the following two values:&lt;br /&gt;
global.param.start.token={{&lt;br /&gt;
global.param.end.token=}}&lt;br /&gt;
&lt;br /&gt;
# Populated by the installer, these values can be used to initialize licence key information into the database.&lt;br /&gt;
com.carfey.obsidian.licence.key=licenceKey&lt;br /&gt;
# Name is only required for hardware-linked or site licenses. Use the name exactly as provided by Carfey Software Corporation in your license email.&lt;br /&gt;
com.carfey.obsidian.licence.name=licenceeName&lt;br /&gt;
&lt;br /&gt;
# As of 2.8.0, if you have issues with classloading (e.g. in Grails or Spring), this flag enables usage of the context classloader .&lt;br /&gt;
com.carfey.jdk.useContextClassLoader=true&lt;br /&gt;
&lt;br /&gt;
# As of 2.9.0, you can set the scheduler host designator name multiple times in the same JVM by using this parameter:&lt;br /&gt;
com.carfey.obsidian.schedulerDesignation=obsidian-dev&lt;br /&gt;
&lt;br /&gt;
# As of 3.0.0, Job Forking can be enabled and configured per node&lt;br /&gt;
com.carfey.obsidian.jvmJobForkingEnabledOnThisNode=true&lt;br /&gt;
# This property is the location of the fork scripts. Obsidian is bundled with obsidianForkedJob.bat and obsidianForkedJob.sh. These are the expected script names.&lt;br /&gt;
com.carfey.obsidian.forkedJobScriptLocation=/Obsidian-3.0.0&lt;br /&gt;
# Use the following optional property if you need to override the default classpath that is built using the contents of the standalone directory.&lt;br /&gt;
# This allows for job forking support in embedded and even webapp deployments. Use the classpath format supported by your operating system.&lt;br /&gt;
#com.carfey.obsidian.forkedJobscriptClasspathOverride=&lt;br /&gt;
# Use the following optional property if you wish to include database configuration parameters as arguments passed to the forking scripts and the target forked Obsidian class.&lt;br /&gt;
# For example, you may need to do this if your configuration is done via System Property overrides or programmmatically in your embedded Obsidian instance.&lt;br /&gt;
#com.carfey.obsidian.jvmJobForkingIncludeDbSysParms=true&lt;br /&gt;
&lt;br /&gt;
# Obsolete as of 4.0.0. As of 3.4.0, the base URL used to resolve links in the Obsidian web app can be overridden. This is typically only required when a load balancer or proxy is used to access Obsidian.&lt;br /&gt;
com.carfey.obsidian.baseHrefUrl=http://myhost:8080/obsidian&lt;br /&gt;
&lt;br /&gt;
# As of 4.3.0, description/parameter formatting is supported. Custom formatter (com.carfey.ops.job.config.formatter.Formatter) supported via this parameter.&lt;br /&gt;
com.carfey.obsidian.formatterClass=com.carfey.ops.job.config.formatter.MarkdownFormatter (default)&lt;br /&gt;
&lt;br /&gt;
# As of 4.4.0, support for running DDL outside Obsidian and applying only the data portion of upgrades. May require incremental upgrades. Contact support for assistance. Enabled with the following:&lt;br /&gt;
com.carfey.obsidian.runner.skipDDL=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.5.0, Slack notifications can be automatically configured for FATAL, ERROR and WARN events by setting this property with a Slack incoming web hook URL. The user name property is optional and defaults to Obsidian Scheduler&lt;br /&gt;
com.carfey.obsidian.slack.webhookUrl=https://hooks.slack.com/services/XXXXXXX/YYYYYYYY/ZZZZZZZZZZ&lt;br /&gt;
com.carfey.obsidian.slack.userName=Obsidian Scheduler (prod)&lt;br /&gt;
&lt;br /&gt;
# As of 4.6.0, Obsidian supports starting a node in paused state. This can also be set as a System property.&lt;br /&gt;
com.carfey.obsidian.schedulerPausedOnStartup=true&lt;br /&gt;
&lt;br /&gt;
# As of 4.10.0, Obsidian supports disabling script engine jobs selectively.&lt;br /&gt;
com.carfey.ops.job.script.BeanShellJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.GroovyJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.RubyJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.PythonJob.enabled=true&lt;br /&gt;
com.carfey.ops.job.script.JavaScriptJob.enabled=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.0.0, Signal interrupt (supported as of 4.5.1) is disabled by default. Use configuration to enable.&lt;br /&gt;
com.carfey.obsidian.signalHandlerActiveForScheduler=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.0, you can send Obsidian events to a REST endpoint via an out-of-the-box Event Hook. The basicAuthorization property is optional, the sample showing its use for myusername:mypassword. The &amp;quot;Basic &amp;quot; prefix is added by Obsidian.&lt;br /&gt;
com.carfey.obsidian.restfulEventHook.url=https://myendpoint.com:1234/rest/obsidian_events&lt;br /&gt;
com.carfey.obsidian.restfulEventHook.basicAuthorization=bXl1c2VybmFtZTpteXBhc3N3b3Jk&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.1, maintenance jobs are automatically scheduled for new installations. Set the following property to disable this behaviour.&lt;br /&gt;
com.carfey.obsidian.skipMaintenanceJobInitialization=true&lt;br /&gt;
&lt;br /&gt;
# As of 5.2.1, you can send Obsidian events to standard output and error streams using an out-of-the-box Event Hook. You can enable the default behaviour and auto-register it by adding the following configuration.&lt;br /&gt;
com.carfey.obsidian.standardOutputStreamsEventHook.enabled=true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Table Prefixes ===&lt;br /&gt;
As shown in full configuration reference, a table name prefix may be specified to create Obsidian tables with names beginning with a specified string.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Table prefix may be used to add a prefix to tables and related database objects. It must be 6 or fewer characters and can contain letters or underscores. Available as of version 2.0.&lt;br /&gt;
com.carfey.obsidian.db.tablePrefix=OBSDN_&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Properties/Yaml File ==&lt;br /&gt;
&lt;br /&gt;
The above configuration must reside in a properties file named &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; or a yaml file named &amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; found according to the rules of [http://docs.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String) ClassLoader.getResource].  If both yaml and properties files are found, yaml is loaded first and properties values will override. Some possibilities include &amp;lt;code&amp;gt;WEB-INF/classes/com.carfey.(properties/yaml)&amp;lt;/code&amp;gt;, a &amp;lt;code&amp;gt;com.carfey.(properties/yaml)&amp;lt;/code&amp;gt; file in a directory which is explicitly added to the classpath, or at the root of a jar file as is done in &amp;lt;code&amp;gt;obsidian-props.jar&amp;lt;/code&amp;gt; for standalone deployments. Prior to Obsidian 3.6, this file had to exist on the classpath, even if only using the override options below to configure Obsidian.&lt;br /&gt;
&lt;br /&gt;
Instead of using a classpath resource for properties, you can specify an external properties file using the Java system property &amp;lt;code&amp;gt;carfey.properties.file&amp;lt;/code&amp;gt;. All expected properties must be specified in either the default &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt; file on the classpath or in the override file. Any properties found in both files will use the override file&#039;s values. Usage: &amp;lt;code&amp;gt;-Dcarfey.properties.file=/home/obsidian/obsidian.properties&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Instead of using a classpath resource for yaml, you can specify an external yaml file using the Java system property &amp;lt;code&amp;gt;carfey.yaml.file&amp;lt;/code&amp;gt;. All expected configuration values must be specified in either the default &amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; file on the classpath or in the override file. Any configuration values found in both files will use the override file&#039;s values. Usage: &amp;lt;code&amp;gt;-Dcarfey.yaml.file=/home/obsidian/obsidian.yaml&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 2.1, you may also use a programmatic properties override. Simply call &amp;lt;code&amp;gt;com.carfey.jdk.sys.Configurator.setOverride(Properties props)&amp;lt;/code&amp;gt; as early as possible in the application startup.  To quote the javadoc of this method, &#039;&#039;For use as a programmatic properties override. This must be called before any classes are accessed either through invocation or class initialization that may require access to configuration done through properties. Best if done as early as possible in code, perhaps first in an entry point class that does little else and then hands-off to existing entry point.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As of Obsidian 4.4, you may specify configuration values as System Properties or Environment Variables. Duplicated values are resolved as follows:&lt;br /&gt;
# System Property&lt;br /&gt;
# Environment Variable - &#039;&#039;&#039;&#039;&#039;overridden by System Property&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
# Properties - &#039;&#039;&#039;&#039;&#039;overridden by Environment Variable and System Property&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Notifications Configuration ==&lt;br /&gt;
&lt;br /&gt;
Sending notifications requires SMTP configuration to be defined in the Obsidian properties file. The properties file reference at the beginning of this page includes SMTP configuration details.&lt;br /&gt;
&lt;br /&gt;
In addition to the properties file, you can configure some Notifications settings to tweak how your emails are sent. These values are configurable under the &#039;&#039;Dispatch&#039;&#039; category of the [[Admin Scheduler Settings|scheduler settings]] screen. Defaults are provided, but you can update them appropriately for your needs.  &lt;br /&gt;
&lt;br /&gt;
=== Notification sender address and DMARC ===&lt;br /&gt;
&lt;br /&gt;
The notificationFromEmail scheduler setting controls the From address used for notification emails. It is available under:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Scheduler Settings → Dispatch → notificationFromEmail&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Set this to an address belonging to your organization and authorized by the SMTP service configured for your Obsidian installation.&lt;br /&gt;
&lt;br /&gt;
Do not use ops-notification@obsidianscheduler.com when sending through your own SMTP infrastructure. Your SMTP server normally cannot authenticate mail for the obsidianscheduler.com domain. This can cause SPF, DKIM, and DMARC failures and may result in notifications being quarantined or rejected.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
scheduler-notifications@example.com&lt;br /&gt;
&lt;br /&gt;
After updating the value, trigger a test notification and confirm successful receipt.&lt;br /&gt;
&lt;br /&gt;
[[File:NotifSettings_4.0.png]]&lt;br /&gt;
&lt;br /&gt;
== Dependent Libraries ==&lt;br /&gt;
&lt;br /&gt;
Obsidian Scheduler requires a number of third party libraries, both for the web administration application and the scheduler itself.  Below is information on these libraries and how they are used.  Unless otherwise noted, they are mandatory. As of Obsidian 2.5.0, our installer supports selective [[Installation_Guide#Configuring_3rd_Party_Library_Conflict_Management|conflict management]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Authentication (OAuth / OIDC)&#039;&#039;&lt;br /&gt;
All libraries in this section are only required when OAuthAuthenticator is active. &#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* oauth2-oidc-sdk-11.23.1.jar, nimbus-jose-jwt-10.0.2.jar. Nimbus OAuth 2.0 SDK with OpenID Connect extensions and JOSE+JWT support.&lt;br /&gt;
* content-type-2.3.jar, lang-tag-1.7.jar. Supporting libraries for the Nimbus SDK.&lt;br /&gt;
* json-smart-2.5.2.jar, accessors-smart-2.5.2.jar. JSON parsing for the Nimbus SDK.&lt;br /&gt;
* jcip-annotations-1.0-1.jar. Concurrency annotations for the Nimbus SDK.&lt;br /&gt;
* asm-9.7.1.jar. Bytecode manipulation library; transitive dependency of the Nimbus SDK.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Authentication (SAML 2.0)&#039;&#039;&lt;br /&gt;
All libraries in this section are only required when SAMLAuthenticator is active; the installer removes them when SAML is not the selected authentication type. &#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* opensaml-core-4.3.2.jar, opensaml-saml-api-4.3.2.jar, opensaml-saml-impl-4.3.2.jar, opensaml-security-api-4.3.2.jar, opensaml-security-impl-4.3.2.jar, opensaml-xmlsec-api-4.3.2.jar, opensaml-xmlsec-impl-4.3.2.jar, opensaml-messaging-api-4.3.2.jar, opensaml-profile-api-4.3.2.jar, opensaml-soap-api-4.3.2.jar, opensaml-soap-impl-4.3.2.jar, opensaml-storage-api-4.3.2.jar. OpenSAML 4 modules implementing SAML 2.0 messages, metadata and signature handling.&lt;br /&gt;
* java-support-8.4.2.jar. Shibboleth utility library required by OpenSAML.&lt;br /&gt;
* xmlsec-2.3.4.jar. Apache Santuario XML Signature implementation.&lt;br /&gt;
* cryptacular-1.2.5.jar, bcprov-jdk18on-1.85.jar, bcpkix-jdk18on-1.85.jar, bcutil-jdk18on-1.85.jar. Cryptographic providers used for assertion signature verification and SP key handling.&lt;br /&gt;
* velocity-engine-core-2.3.jar. Template engine used by OpenSAML&#039;s message encoders.&lt;br /&gt;
* commons-lang3-3.20.0.jar, commons-codec-1.15.jar. Apache Commons utilities required by OpenSAML.&lt;br /&gt;
* httpclient-4.5.14.jar, httpcore-4.4.16.jar. HTTP client used to retrieve IdP federation metadata.&lt;br /&gt;
* guava-32.1.3-jre.jar, failureaccess-1.0.1.jar, listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar. Guava collections and its companion placeholder artifacts, required by OpenSAML.&lt;br /&gt;
* metrics-core-4.2.25.jar. Dropwizard Metrics, required by OpenSAML.&lt;br /&gt;
* slf4j-api-2.0.12.jar, log4j-slf4j2-impl-2.26.0.jar. SLF4J API and its log4j2 binding, so OpenSAML&#039;s diagnostic logging is routed into Obsidian&#039;s log4j2 configuration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Embedded Tomcat (Obsidian Embedded Tomcat JAR packs only)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* tomcat-embed-core-10.1.39.jar, tomcat-embed-el-10.1.39.jar — Apache Tomcat embedded components (Apache License 2.0). Present in &#039;&#039;&#039;embed-tomcat-lib/&#039;&#039;&#039; when an Embedded Tomcat pack is selected.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Common&#039;&#039;&lt;br /&gt;
* obsidian.jar.  Core Obsidian lib.&lt;br /&gt;
* gson-2.10.1. Supports REST API and web administration.&lt;br /&gt;
* jmustache-1.15.jar. Provides email templating.&lt;br /&gt;
* log4j-api-2.26.0.jar, log4j-core-2.26.0.jar.  Obsidian logging uses log4j2.&lt;br /&gt;
* snakeyaml-2.3.jar. Support for YAML configuration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Databases&#039;&#039;&lt;br /&gt;
* h2-1.4.200.jar.  H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* mariadb-java-client-2.7.2.jar. MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* mssql-jdbc-9.2.1.jre11.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc11-21.1.0.0.jar. Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-42.2.19.jar.  PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Mail&#039;&#039; &lt;br /&gt;
* activation-1.1.jar, javax.mail-1.6.2.jar. Used for javamail (javax) email notifications.&lt;br /&gt;
* angus-activation-2.0.1.jar, angus-mail-2.0.2.jar. Optionally used with Jakarta EE email notifications.&lt;br /&gt;
* jakarta.activation-api-2.1.2.jar, jakarta.mail-api-2.1.2.jar. Used for Jakarta EE email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Scripts&#039;&#039;&lt;br /&gt;
* bsh-2.0b5.jar.  Support for our Bean Shell script execution. (http://www.beanshell.org).  Only required for BeanShellJob usage.&lt;br /&gt;
* jruby-9.2.17.0.jar. Support for Ruby script execution (https://www.jruby.org/). Only required for RubyJob usage.&lt;br /&gt;
* jython-standalone-2.7.2.jar. Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Scripts - Prior to Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Groovy scripting shipped as a single uber archive: apache-groovy-all-4.0.24.jar&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Scripts - As of Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Groovy scripting ships as 13 modular JARs: groovy-4.0.24.jar, groovy-astbuilder-4.0.24.jar, groovy-datetime-4.0.24.jar, groovy-dateutil-4.0.24.jar, groovy-json-4.0.24.jar, groovy-jsr223-4.0.24.jar, groovy-macro-4.0.24.jar, groovy-macro-library-4.0.24.jar, groovy-nio-4.0.24.jar, groovy-sql-4.0.24.jar, groovy-templates-4.0.24.jar, groovy-typecheckers-4.0.24.jar, groovy-xml-4.0.24.jar.&lt;br /&gt;
Plus four owned transitives (included when Groovy is enabled): asm-analysis-9.7.1.jar, asm-tree-9.7.1.jar, asm-util-9.7.1.jar, javaparser-core-3.26.2.jar&lt;br /&gt;
All 17 JARs above are excluded when Groovy scripting is disabled at install time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Web Admin&#039;&#039;&lt;br /&gt;
* dom4j-2.1.3.jar.  XML utilities.&lt;br /&gt;
* flexmark-0.62.2.jar, flexmark-util-0.62.2.jar. Markdown formatting for Description and Parameter job annotations.&lt;br /&gt;
* jxl-2.6.12.jar. Excel file format utilities. Only required in web administration.&lt;br /&gt;
* opencsv-5.4.jar. Support for CSV downloads in UI.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Web Admin - As of Obsidian 7.0.0:&#039;&#039;&#039;&lt;br /&gt;
Wiki rendering ships as 12 modular JARs:&lt;br /&gt;
flexmark-0.62.2.jar, flexmark-util-ast-0.62.2.jar, flexmark-util-builder-0.62.2.jar, flexmark-util-collection-0.62.2.jar, flexmark-util-data-0.62.2.jar, flexmark-util-dependency-0.62.2.jar, flexmark-util-format-0.62.2.jar, flexmark-util-html-0.62.2.jar, flexmark-util-misc-0.62.2.jar, flexmark-util-options-0.62.2.jar, flexmark-util-sequence-0.62.2.jar, flexmark-util-visitor-0.62.2.jar&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;As of Obsidian 7.0.0&#039;&#039;&#039;, the following changes were made:&lt;br /&gt;
* &#039;&#039;&#039;log4j upgraded&#039;&#039;&#039;: log4j-api and log4j-core updated from 2.17.1 to 2.26.0.&lt;br /&gt;
* &#039;&#039;&#039;Modular Groovy libraries&#039;&#039;&#039;: Obsidian no longer ships &#039;&#039;&#039;apache-groovy-all-4.0.24.jar&#039;&#039;&#039;. Groovy scripting now uses 13 modular JARs (see &#039;&#039;Scripts&#039;&#039; above). When Groovy is disabled at install time, all 13 modules are excluded.&lt;br /&gt;
* &#039;&#039;&#039;Modular Flexmark libraries&#039;&#039;&#039;: Obsidian no longer ships &#039;&#039;&#039;flexmark-util-0.62.2.jar&#039;&#039;&#039;. Wiki rendering now uses 12 modular Flexmark JARs (see &#039;&#039;Scripts&#039;&#039; above). &lt;br /&gt;
* &#039;&#039;&#039;OAuth / OIDC authentication libraries&#039;&#039;&#039; added (Nimbus OAuth 2.0 SDK and transitive dependencies). Required only when OAuthAuthenticator is configured. See [[Advanced_Configuration#OAuth_/_OIDC_Authentication_Properties|Authentication (OAuth/OIDC)]] above.&lt;br /&gt;
* &#039;&#039;&#039;SAML 2.0 authentication libraries&#039;&#039;&#039; added (OpenSAML 4 and its transitive dependencies, plus the SLF4J-to-log4j2 binding). Required only when SAMLAuthenticator is configured. See [[Advanced_Configuration#SAML_Authentication_Properties|Authentication (SAML 2.0)]] above.&lt;br /&gt;
* Embedded Tomcat runtime added (tomcat-embed-core-10.1.39.jar, tomcat-embed-el-10.1.39.jar). Present only when an Embedded Tomcat pack is selected. Replaces the Jetty quick-start distribution which has been removed.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 7.0.0&#039;&#039;&#039;, the following libraries were in use.&lt;br /&gt;
* log4j-api-2.17.1.jar, log4j-core-2.17.1.jar.  Obsidian logging uses log4j2.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 6.0.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* groovy-all-3.0.7.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.4.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* gson-2.8.6.jar. Supports REST API and web administration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.2.0&#039;&#039;&#039;, the following library was in use.&lt;br /&gt;
* jakarta.activation-1.2.1.jar. Used for email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.0.3&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* &#039;&#039;Obsidian 5.0.2&#039;&#039; log4j-api-2.16.0.jar, log4j-core-2.16.0.jar.  &lt;br /&gt;
* &#039;&#039;Obsidian 5.0.1&#039;&#039; log4j-api-2.15.0.jar, log4j-core-2.15.0.jar.  &lt;br /&gt;
* &#039;&#039;Obsidian 5.0.0&#039;&#039; log4j-api-2.14.1.jar, log4j-core-2.14.1.jar.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 5.0.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* activation-1.1.jar, javax.mail-1.5.5.jar.  Used for email notifications.&lt;br /&gt;
* dom4j-1.6.1.jar. XML utilities.&lt;br /&gt;
* gson-2.7.jar. Supports REST API and web administration.&lt;br /&gt;
* bsh-2.0b4.jar.  Support for our Bean Shell script execution. (http://www.beanshell.org).  Only required for BeanShellJob usage.&lt;br /&gt;
* groovy-all-2.4.14.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
* jython-standalone-2.7.0.jar. Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
* jruby-complete-9.2.7.0.jar. Support for Ruby script execution (https://www.jruby.org/). Only required for RubyJob usage.&lt;br /&gt;
* jmustache-1.12.jar. Provides email templating.&lt;br /&gt;
* h2-1.4.192.  H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* log4j-1.2.9.jar.  Obsidian logging uses log4j.&lt;br /&gt;
* mariadb-java-client-1.4.5.jar.  MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* jtds-1.3.1.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc7-12.1.0.1.jar.  Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-9.4.1208.jre7.jar. PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
* opencsv-3.8.jar. Support for CSV downloads in UI.&lt;br /&gt;
* flexmark-0.19.6.jar, flexmark-util-0.19.6.jar (as of 4.3.0). Markdown formatting for Description and Parameter job annotations.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 4.5.1&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* groovy-all-2.4.7.jar. Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 4.0.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* javax.mail-1.5.2.jar.  Used for email notifications.&lt;br /&gt;
* jstl.jar, standard.jar.  Web utilities. Only required in web administration.&lt;br /&gt;
* gson-2.2.2.jar. Supports REST API and web administration.&lt;br /&gt;
* groovy-all-2.1.8.jar.  Support for Groovy script execution (https://groovy-lang.org/).  Only required for GroovyJob usage.&lt;br /&gt;
* jython-standalone-2.5.3.jar.  Support for Python script execution (https://www.jython.org/).  Only required for PythonJob usage.&lt;br /&gt;
* jmustache-1.8.jar. Provides email templating.&lt;br /&gt;
* h2-1.3.154. H2 database and JDBC driver.  Only required for running H2.&lt;br /&gt;
* mariadb-java-client-1.1.5.jar. MySQL JDBC driver. Only required for running against MySQL or MariaDB.&lt;br /&gt;
* jtds-1.2.8.jar. SQL Server JDBC driver.  Only required for running SQLServer.&lt;br /&gt;
* ojdbc6-11.2.0.3.jar. Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
* postgresql-9.0-801.jdbc4.jar.  PostgreSQL JDBC driver.  Only required for running PostgreSQL.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.9.1&#039;&#039;&#039;, the following redundant library was in included in the Obsidian packaging.&lt;br /&gt;
* smtp.jar.  Used for email notifications.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.2.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* groovy-all-1.7.6.jar&lt;br /&gt;
* jython.jar (version 2.5.2rc2)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.1.1&#039;&#039;&#039;, the following libraries were also included.&lt;br /&gt;
* carfey-date-1.2.jar or carfey-date-1.1.jar.  Date math/manipulation.&lt;br /&gt;
* jdk-gen.jar, jdk.jar, suite-gen.jar, suite.jar.  Core Obsidian libs.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 2.1.0&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* ojdbc14.jar Oracle JDBC driver.  Only required for running Oracle.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Prior to Obsidian 1.5&#039;&#039;&#039;, the following older libraries were in use.&lt;br /&gt;
* gson-1.5.jar. Supports REST API and web administration.&lt;br /&gt;
&lt;br /&gt;
=== Required Libraries for Embedded API ===&lt;br /&gt;
&lt;br /&gt;
To use the [[Embedded_API|Embedded API]], the following resources should be imported. If you have newer versions of the same JARs in your application, feel free to use the newer version. The JAR files below can generally be found in the &#039;&#039;standalone&#039;&#039; directory if your installation. The &amp;lt;code&amp;gt;com.carfey.properties&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;com.carfey.yaml&amp;lt;/code&amp;gt; file can be extracted from the &amp;lt;code&amp;gt;obsidian-properties-configuration.jar&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;obsidian-yaml-configuration.jar&amp;lt;/code&amp;gt; in the &#039;&#039;standalone&#039;&#039; directory, or from an Obsidian WAR file under &amp;lt;code&amp;gt;WEB-INF/classes&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* com.carfey.properties/com.carfey.yaml (configuration file), or obsidian-properties-configuration.jar/obsidian-yaml-configuration.jar, which contains the configuration file&lt;br /&gt;
* obsidian.jar&lt;br /&gt;
* dom4j-2.1.3.jar&lt;br /&gt;
* flexmark-0.62.2.jar plus the modular Flexmark JARs listed under &#039;&#039;Web Admin&#039;&#039; above&lt;br /&gt;
* gson-2.10.1&lt;br /&gt;
* log4j-api-2.26.0.jar, log4j-core-2.26.0.jar&lt;br /&gt;
* Appropriate JDBC JAR (e.g. mariadb-java-client-2.7.2.jar for MySQL or MariaDB)&lt;br /&gt;
&lt;br /&gt;
In addition, the following resources should be included if you wish to have notification support enabled when using the API:&lt;br /&gt;
* jmustache-1.15.jar&lt;br /&gt;
** jakarta.activation-api-2.1.2.jar, jakarta.mail-api-2.1.2.jar. Used for Jakarta EE email notifications.&lt;br /&gt;
** angus-activation-2.0.1.jar, angus-mail-2.0.2.jar. Optionally used with Jakarta EE email notifications.&lt;br /&gt;
** activation-1.1.jar, javax.mail-1.6.2.jar. Used for javamail (javax) email notifications.&lt;br /&gt;
&lt;br /&gt;
Finally, if you need to configure scripting jobs from the API, you will need to include any applicable JARs from the following list:&lt;br /&gt;
* apache-groovy-all-4.0.24.jar&lt;br /&gt;
* bsh-2.0b5.jar&lt;br /&gt;
* jruby-9.2.17.0.jar&lt;br /&gt;
* jython-standalone-2.7.2.jar&lt;br /&gt;
&lt;br /&gt;
== Disabling Job Execution &amp;amp; Scheduling in the Web Application ==&lt;br /&gt;
See the instructions within the [[Getting_Started#Disabling_Job_Scheduling_in_the_Web_Application|Getting Started Guide]].&lt;br /&gt;
&lt;br /&gt;
== Disabling Automatic Database Updates ==&lt;br /&gt;
See the instructions within the [[Getting_Started#Disabling_Automatic_Database_Updates|Getting Started Guide]].&lt;/div&gt;</summary>
		<author><name>Craig</name></author>
	</entry>
</feed>