{"id":12490,"date":"2019-06-16T18:36:24","date_gmt":"2019-06-16T16:36:24","guid":{"rendered":"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/"},"modified":"2025-10-24T09:32:14","modified_gmt":"2025-10-24T07:32:14","slug":"connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i","status":"publish","type":"post","link":"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/","title":{"rendered":"Connecting to a Repository via a Dynamically Edited dfc.properties File (part I)"},"content":{"rendered":"<p>Now that we have containerized content servers, it is very easy, maybe too easy, to create new repositories. Their creation is still not any faster (whether they are containerized or not is irrelevant here) but given a configuration file it just takes one command to instantiate an image into a running container with working repositories in it. Thus, during experimentation and testing, out of laziness or in a hurry, one can quickly finish up having several containers with identically named repositories, e.g. dmtest01, with an identically named docbroker, e.g. docbroker01. Now, suppose one wants to connect to the docbase dmtest01 running on the 3rd such container using the familiar command-line tools idql\/iapi\/dmawk. How then to select that particular instance of dmtest01 among all the others ?<br \/>\nTo precise the test case, let&#8217;s say that we are using a custom bridge network to link the containers together on the docker host (appropriately named docker) which is a VirtualBox VM running an Ubuntu flavor. The metal also runs natively the same Ubuntu distro. It looks complicated but actually matches the common on-premises infrastructure type where the metal is an ESX or equivalent, its O\/S is the hypervisor and the VMs run a Redhat or Suse distro. As this is a local testing environment, no DNS or network customizations have been introduced save for the custom bridge.<br \/>\nWe want to reach a remote repository either from container to container or from container to host or from host to container.<br \/>\nThe problem here stems from the lack of flexibility in the docbroker\/dfc.properties file mechanism and no network fiddling can work around this.<\/p>\n<h2>It&#8217;s All in The dfc.properties File<\/h2>\n<p>Containers have distinct host names, so suffice it to edit their local dfc.properties file and edit this field only. Their file may all look like the one below:<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1; highlight: [3,4]\">\ndfc.docbroker.host[0]=container01\ndfc.docbroker.port[0]=1489\ndfc.docbroker.host[1]=docker\ndfc.docbroker.port[1]=1489\ndfc.docbroker.host[3]=container011\ndfc.docbroker.port[3]=1489\ndfc.docbroker.host[4]=container02\ndfc.docbroker.port[4]=1489\n<\/pre>\n<p>In effect, the custom bridge network embeds a DNS for all the attached containers, so their host names are known to each other (but not to the host so IP address must be used from there or the host&#8217;s \/etc\/hosts file must be edited). The docbroker ports are the ones inside the containers and have all the same value 1489 because they were created out of the same configuration files. The docker entry has been added to the containers&#8217; \/etc\/host file via the &ndash;&ndash;add-host= clause of the docker run&#8217;s command.<br \/>\nFor the containers&#8217; host machine, where a Documentum repository has been installed too, the dfc.properties file could look like this one:<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1; highlight: []\">\ndfc.docbroker.host[0]=docker\ndfc.docbroker.port[0]=1489\ndfc.docbroker.host[1]=docker\ndfc.docbroker.port[1]=2489\ndfc.docbroker.host[3]=docker\ndfc.docbroker.port[3]=3489\ndfc.docbroker.host[4]=docker\ndfc.docbroker.port[4]=5489\n<\/pre>\n<p>Here, the host name is the one of the VM where the containers sit and is the same for all the containers. The port numbers differ because they are the external container&#8217;s port which are published in the host VM and mapped to the respective docbroker&#8217;s internal port, 1489. Since the containers share the same custom network, their host names, IP addresses and external ports must all be different when running the image, or docker won&#8217;t allow it.<br \/>\nAlternatively, the container&#8217;s IP addresses and internal docbroker&#8217;s ports could be used directly too if one is too lazy to declare the containers&#8217; host names in the host&#8217;s \/etc\/hosts file, which is generally the case when testing:<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1; highlight: []\">\ndfc.docbroker.host[0]=docker \ndfc.docbroker.port[0]=1489\ndfc.docbroker.host[1]=192.168.33.101\ndfc.docbroker.port[1]=1489\ndfc.docbroker.host[2]=192.168.33.102\ndfc.docbroker.port[2]=1489\ndfc.docbroker.host[3]=192.168.33.104\ndfc.docbroker.port[3]=1489\n<\/pre>\n<p>The host&#8217;s custom network will take care of routing the traffic into the respective containers.<br \/>\nCan you spot the problem now ? As all the containers contain identically named repositories (for clarity, let&#8217;s say that we are looking for the docbase dmtest01), the first contacted docbroker in that file will always reply successfully because there is indeed a dmtest01 docbase in that container and consequently one will always be directed to the docbase container01.dmtest01. If one wants to contact container03.dmtest01, this configuration won&#8217;t let do it. One would need to edit it and move the target container03 host in the first position, which is OK until one wants to access container02.dmtest01 or go back to container01.dmtest01.<br \/>\nThis situation has been existing forever but containers make it more obvious because they make it so much easier to have repository homonyms.<br \/>\nSo is there a simpler way to work around this limitation than editing back and forth a configuration file or giving different names to the containerized repositories ?<\/p>\n<h2>A Few Reminders<\/h2>\n<p>Documentum has made quite a lot of design decisions inspired by the Oracle DBMS but their implementation is far from offering the same level of flexibility and power, and this is often irritating. Let&#8217;s consider the connectivity for example. Simply speaking, Oracle&#8217;s SQL*Net configuration relies mainly on a tnsnames.ora file for the connectivity (it can also use a centralized ldap server but let&#8217;s keep it simple). This file contains entries used to contact listeners and get the information needed to connect to the related database. Minimal data to provide in the entries are the listener&#8217;s hostname and port, and the database sid or service name, e.g.:<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1; highlight: []\">\n...\nORCL =\n  (DESCRIPTION =\n    (ADDRESS = (PROTOCOL = TCP)(HOST = db)(PORT = 1521))\n    (CONNECT_DATA =\n      (SERVER = DEDICATED)\n      (SERVICE_NAME = db_service)\n    )\n  )\n...\n<\/pre>\n<p>A connection to the database db_service can simply be requested as follows:<br \/>\n<code><br \/>\nsqlplus scott@orcl<br \/>\n<\/code><br \/>\norcl is the SQL*Net alias for the database served by db_service. It works like an index in a lookup table, the tnsnames.ora file.<br \/>\nCompare this with a typical dfc.properties file, e.g. \/home\/dmadmin\/documentum\/shared\/config\/dfc.properties:<br \/>\n<code><br \/>\n...<br \/>\ndfc.docbroker.host[0]=docker<br \/>\ndfc.docbroker.port[0]=1489<br \/>\ndfc.docbroker.host[1]=dmtest<br \/>\ndfc.docbroker.port[1]=1489<br \/>\n...<br \/>\n<\/code><br \/>\nSimilarly, instead of contacting listeners, we have here docbrokers. A connection to the docbase dmtest can be requested as follows:<br \/>\n<code><br \/>\nidql dmtest<br \/>\n<\/code><br \/>\ndmtest is the target repository. It is not a lookup key in the dfc.properties file. Unlike the tnsnames.ora file and its aliases, there is an indirection here and the dfc.properties file does not directly tell where to find a certain repository, it just lists the docbrokers to be sequentially queried about it until the first one that knows the repository (or an homonym thereof) answers. If the returned target docbase is the wrong homonym, tough luck, it will not be reachable, unless the order of the entries is changed. Repositories announces themselves to the docbrokers by &#8220;projecting&#8221; themselves. If two repositories by the same name project to the same docbroker, no error is raised but the docbroker can return unexpected results, e.g. one may finish up in the unintended docbase.<br \/>\nAnother major difference is that with Oracle but not with Documentum, it is possible to bypass the tnsnames.ora file by specifying the connection data in-line, e.g. on the command-line:<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1; highlight: []\">\nsqlplus scott@'(DESCRIPTION =\n    (ADDRESS = (PROTOCOL = TCP)(HOST = db)(PORT = 1521))\n    (CONNECT_DATA =\n      (SERVER = DEDICATED)\n      (SERVICE_NAME = db_service)\n    )\n  )'\n<\/pre>\n<p>This can be very useful when editing the local, official listener.ora file is not allowed, and sometimes faster than setting $TNS_ADMIN to an accessible local directory and editing a private listener.ora file there.<br \/>\nThis annoyance is even more frustrating because Documentum&#8217;s command-line tools do support a similar syntax but for a different purpose:<br \/>\n<code><br \/>\nidql repository[.service][@machine] [other parameters]<br \/>\n<\/code><br \/>\nWhile this syntax is logically useful to access the service (akin to an Oracle&#8217;s instance but for a HA Documentum installation), it is used in a distributed repository environment to contact a particular node&#8217;s docbroker; however, it still does not work if that docbroker is not first declared in the local dfc.properties file.<br \/>\nLast but not least, one more reason to be frustrated is that the DfCs do allow to choose a specific docbroker when opening a session, as illustrated by the jython snippet below:<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1; highlight: [15,16]\">\nimport traceback\nimport com.documentum.fc.client as DFCClient\nimport com.documentum.fc.common as DFCCommon\n\ndocbroker_host = \"docker\"\ndocbroker_port = \"1489\"\ndocbase = \"dmtest\"\nusername = \"dmadmin\"\npassword = \"dmadmin\"\nprint(\"attempting to connect to \" + docbase + \" as \" + username + \"\/\" + password + \" via docbroker on host \" + docbroker_host + \":\" + docbroker_port)\ntry:\n  client = DFCClient.DfClient.getLocalClient()\n\n  config = client.getClientConfig()\n  config.setString (\"primary_host\", docbroker_host)\n  config.setString (\"primary_port\", docbroker_port)\n\n  logInfo = DFCCommon.DfLoginInfo()\n  logInfo.setUser(username)\n  logInfo.setPassword(password)\n  docbase_session = client.newSession(docbase, logInfo)\n\n  if docbase_session is not None:\n    print(\"Connected !\")\n  else:\n    print(\"Couldn't connect !\")\nexcept Exception:\n  traceback.print_exc()\n<\/pre>\n<p>Content of dfc.properties:<br \/>\n<code><br \/>\n$ cat documentum\/shared\/config\/dfc.properties<br \/>\ndfc.date_format=dd.MM.yyyy HH:mm:ss<br \/>\n<\/code><br \/>\nExecution:<br \/>\n<code><br \/>\n$ jython .\/test.jy<br \/>\n...<br \/>\nattempting to connect to dmtest as dmadmin\/dmadmin via docbroker docker<br \/>\nConnected !<br \/>\n<\/code><br \/>\nDespite a dfc.properties file devoid of any docbroker definition, the connection was successful. Unfortunately, this convenience has not been carried over to the vegetative command-line tools.<br \/>\nWhile we can dream and hope for those tools to be resurrected and a backport miracle to happen (are you listening OTX ?), the next best thing is to tackle ourselves this shortcoming and implement an as unobtrusive as possible solution. Let&#8217;s see how.<\/p>\n<h2>A few Proposals<\/h2>\n<p>Currently, one has to manually edit the local dfc.properties file, but this is tedious to say the least, because changes must sometimes be done twice, forwards and rolled back if the change is only temporary. To avoid this, we could add  at once in our local dfc.properties file all the machines that host repositories of interest but this file could quickly grow large and it won&#8217;t solve the case of repository homonyms. The situation would become quite unmanageable although an environment variable such as the late DMCL_CONFIG (appropriately revamped e.g. to DFC_PROPERTIES_CONFIG for the full path name of the dfc.properties file to use) could help here to organize those entries. But there is not such a variable any longer for the command-line tools (those tools have stopped evolving since CS v6.x) although there is a property for the DfCs to pass to the JVM at startup, -Ddfc.properties.file, or even the #include clause in the dfc.properties file, or playing with the $CLASSPATH but there is a better way.<br \/>\nWhat about an on-the-fly, transparent, behind the scenes dfc.properties file editing to support a connection syntax similar to the Oracle&#8217;s in-line one ?<br \/>\n<strong>Proposal 1<\/strong><br \/>\nLet&#8217;s specify the address of the docbroker of interest directly on the command-line, as follows:<br \/>\n<code><br \/>\n$ idql dmtest01@container03:3489<br \/>\nor<br \/>\n$ idql dmtest01@192.168.33.104:3489<br \/>\n<\/code><br \/>\nThis is more akin to Oracle in-line connection syntax above.<br \/>\n<strong>Proposal 2<\/strong><br \/>\nAn alternative could be to use an Oracle&#8217;s tnsnames.ora-like configuration file such as the one below (and (in (keeping (with (the (lisp spirit)))))):<br \/>\n<code><br \/>\ndmtest01 = ((docbroker.host = container01) (docbroker.port = 1489))<br \/>\ndmtest02 = ((docbroker.host = container02) (docbroker.port = 1489))<br \/>\ndmtest03 = ((docbroker.host = container03) (docbroker.port = 1489))<br \/>\n<\/code><br \/>\nand to use it thusly:<br \/>\n<code><br \/>\n$ idql dmtest01@dmtest03<br \/>\n<\/code><br \/>\ndmtest03 is looked up in the configuration file and replaced on the command-line by its definition.<br \/>\n<strong>Proposal 3<\/strong><br \/>\nWith a more concise configuration file that can also be sourced:<br \/>\n<code><br \/>\ndmtest01=container01:1489<br \/>\ndmtest02=container02:1489<br \/>\ndmtest03=container03:1489<br \/>\n<\/code><br \/>\nand used as follows:<br \/>\n<code><br \/>\n$ export REPO_ALIAS=~\/repository_connections.aliases<br \/>\n$ . $REPO_ALIAS<br \/>\n$ .\/widql dmtest01@$dmtest03<br \/>\n<\/code><br \/>\n$dmtest03 is directly fetched from the environment after the configuration file has been sourced, which is equivalent to a lookup. Since the variable substitution occurs at the shell level, it comes free of charge.<br \/>\nWith a bit more generalization, it is possible to merge the three proposals together:<br \/>\n<code><br \/>\n$ idql repository(@host_literal:port_number) | @$target<br \/>\n<\/code><br \/>\nIn other words, one can either provide literally the full connection information or provide a variable which will be resolved by the shell from a configuration file to be sourced preliminarily.<br \/>\nLet&#8217;s push the configuration file a bit farther and define complete aliases up to the repository name like this:<br \/>\n<code><br \/>\ndmtest=dmtest@docker:1489<br \/>\nor even so:<br \/>\ndmtest=dmtest:docker:1489<br \/>\n<\/code><br \/>\nUsage:<br \/>\n<code><br \/>\n$ .\/widql $dmtest<br \/>\n<\/code><br \/>\nThe shell will expand the alias with its definition. The good thing is the definition styles can be mixed and matched to suit one&#8217;s fantasy. Example of a configuration file:<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1; highlight: [5-11]\">\n# must be sourced prior so the environment variables can be resolved;\n# this is a enhancement over the dfc.properties file syntax used by the dctm_wrapper utility:\n# docbroker.host[i]=...\n# docbroker.port[i]=...\n# it supports several syntaxes:\n# docbroker only definition docbroker_host:port;\n#    usage: .\/widql dmtest@$dmtest\n# full definition docbase[@[docbroker_host]:[port]]\n#    usage: .\/widql $test\n# alternate ':' separator docbase:[docbroker_host]:[port];\n#    usage: .\/widql $dmtestVM\n# alias literal;\n#    usage: .\/widql test\n# in order to resolve alias literals, the wrapper will source the configuration file by itself;\n\n# docker.dmtest;\n# docbroker only definition;\nd_dmtest=docker:1489\n# full definition;\nf_dmtest=dmtest@docker:1489\n# alternate ':' separator;\na_dmtest=dmtest:docker:1489\n\n# container01.dmtest01;\n# docbroker only definition;\nd_dmtest01=container01:2489\ndip_dmtest01=192.168.33.101:1489\n# full definition;\nf_dmtest01=dmtest01@container01:2489\nfip_dmtest01c=dmtest01@192.168.33.101:1489\n# alternate ':' separator;\na_dmtest01=dmtest01:container01:2489\naip_dmtest01=dmtest01:192.168.33.101:2489\n\n# container011.dmtest01;\n# docbroker only definition;\nd_dmtest011=container011:5489\ndip_dmtest011=192.168.33.104:1489\n# full definition;\nf_dmtest011=dmtest01@container011:2489\nfip_dmtest011=dmtest01@192.168.33.104:1489\n# alternate ':' separator;\na_dmtest011=dmtest01:container011:2489\naip_dmtest011=dmtest01:192.168.33.104:2489\n<\/pre>\n<p>Lines 5 to 14 explains all the supported target syntaxes with a new one presented on lines 12 to 14, which will be explained later in the paragraph entitled Possible Enhancements.<br \/>\nUsing lookup variables in a configuration file makes things easier when the host names are hard to remember because better mnemonic aliases can be defined for them. Also, as they are looked up, the entries can be in any order. They must obviously be unique or they will mask each other. A consistent naming convention may be required to easily find one own&#8217;s way into this file.<br \/>\nWhenever the enhanced syntax is used, it triggers an automatic editing of the dfc.properties file and the specified connection information is inserted as dfc.docbroker.host and dfc.docbroker.port entries. Then, the corresponding Documentum tool gets invoked and finally the original dfc.properties file is restored when the tool exits. The trigger here is the presence of the @ or : characters in the first command-line parameter.<br \/>\nThis would also cover the case when an entry is simply missing from the dfc.properties file. Actually, from the point of view of the command-line tools, all the connection definitions could be handled over to the new configuration file and even removed from dfc.properties as they are dynamically added to and deleted from the latter file as needed.<\/p>\n<h2>The Implementation<\/h2>\n<p>The above proposal looks pretty easy and fun to implement, so let&#8217;s give it a shot. In this article, I&#8217;ll present a little script, dctm_wrapper, that builds upon the above @syntax to first edit the configuration file on demand (that&#8217;s the dynamic part of the article&#8217;s title) and then invoke the standard idql, iapi or dmawk utilities, with an optional rollback of the change on exiting.<br \/>\nSince it is not possible to bypass the dfc.properties files, we will dynamically modify it whenever the @host syntax is used from a command-line tool. As we do no want to replace the official idql, iapi and dmawk tools, yet, we will create new ones, say widql, wiapi and wdmawk (where w stands for wrapper). Those will be symlinks to the real script, dctm-wrapper.sh, which will be able to invoke either idql, iapi or dmawk according to how it was called (bash&#8217;s $0 contains the name of the symlink that was invoked, even though its target is always dctm-wrapper.sh, see the script&#8217;s source at the next paragraph).<br \/>\nThe script dctm-wrapper.sh will support the following syntax:<br \/>\n<code><br \/>\n$ .\/widql docbase[@[host][:port]] [other standard parameters] [--verbose] [--append] [--keep]<br \/>\n$ .\/wiapi docbase[@[host][:port]] [other standard parameters] [--verbose] [--append] [--keep]<br \/>\n$ .\/wdmawk [-v] docbase[@[host][:port]] [dmawk parameters] [--verbose] [--append] [--keep]<br \/>\n<\/code><br \/>\nThe custom parameters &ndash;&ndash;verbose, &ndash;&ndash;append and &ndash;&ndash;keep are processed by the script and stripped off before invoking the official tools.<br \/>\nwdmawk is a bit special in that the native tool, dmawk, is invoked differently from iapi\/idql but I felt that it too could benefit from this little hack. Therefore, in addition to the non-interactive editing of the dfc.properties file, wdmawk also passes on the target docbase name as a -v docbase=&#8230; command-line parameter (the standard way to pass parameters in awk) and removes the extended target parameter docbase[@[host][:port]] unless it is prefixed by the -v option in which case it gets forwarded through the -v repo_target= parameter. The dmawk program is then free to use them the way it likes. The repo_target parameter could have been specified on the command-line independently but the -v option can still be useful in cases such as the one below:<br \/>\n<code><br \/>\n$ .\/wdmawk docbase@docker:1489 -v repo_target=docbase@docker:1489 '{....}'<br \/>\n<\/code><br \/>\nwhich can be shortened to<br \/>\n<code><br \/>\n$ .\/wdmawk -v docbase@docker:1489 '{....}'<br \/>\n<\/code><br \/>\nIf the extended target docbase parameter is present, it must be the first one.<br \/>\nIf the &#8216;@&#8217; or &#8216;:&#8217; characters are missing, it means the enhanced syntax is not used and the script will not attempt to modify dfc.properties; it will pass on all the remaining parameters to the matching official tools.<br \/>\nWhen @[host][:port] is present, the dfc.properties file will be edited to accommodate the new docbroker&#8217;s parameters; all the existing couples dfc.docbroker.host\/dfc.docbroker.port will either be removed (if &ndash;&ndash;append is missing) or preserved (if &ndash;&ndash;append is present) and a new couple entry will be appended with the given values. Obviously, if one want to avoid the homonym trap, &ndash;&ndash;append should not be used in order to let the given docbroker be picked up as the sole entry in the property file.<br \/>\nWhen &ndash;&ndash;append and &ndash;&ndash;keep are present, we end up with a convenient way to add docbroker entries into the property file without manually editing it.<br \/>\nAs the host is optional, it can be omitted and the one from the first dfc.docbroker.host[] entry will be used instead. Ditto for the port.<br \/>\nNormally, upon returning from the invocation of the original tools, the former dfc.properties file is restored to its original content. However, if &ndash;&ndash;keep is mentioned, the rollback will not be performed and the modified file will replace the original file. The latter will still be there though but renamed to $DOCUMENTUM_SHARED\/config\/dfc.properties_saved_YY-MM-DD_HH:MI:SS so it will still be possible to manually roll back. &ndash;&ndash;keep is mostly useful in conjunction with &ndash;&ndash;append so that new docbrokers get permanently added to the configuration file.<br \/>\nFinally, when &ndash;&ndash;verbose is specified, the changes to the dfc.properties file will be sent to stdout; a diff of both the original and the new configuration file will also be shown, along with the final command-line used to invoke the selected original tool. This helps troubleshooting possible command-line parsing issues because, as it can be seen from the code, no extra-effort has been put into this section.<\/p>\n<h2>The Code<\/h2>\n<p>The script below shows a possible implementation:<\/p>\n<pre class=\"brush: bash; gutter: true; first-line: 1; highlight: []\">\n#!\/bin\/bash\n# Installation:\n# it should mainly be called through one of the aliases below for the standard tools instead:\n# ln -s dctm-wrapper wiapi\n# ln -s dctm-wrapper widql\n# ln -s dctm-wrapper wdmawk\n# where the initial w stands for wrapper;\n# and then:\n#    .\/widql ...\n# if called directly, no changes are commited in the dfc.properties file; instead, its altered content is simply output to sdtout; this lets any Unix account to use the properties file and generate its own updated variant out of it;  \n# The full path of the dfc.properties file is pointed to by $DFC_CONFIG if defined, otherwise by $DOCUMENTUM_SHARED;\n# if neither $DFC_CONFIG nor $DOCUMENTUM_SHARED is defined, the scripts errors out;\n# Since there is no $DOCUMENTUM_SHARED in eCS &#x2265; 16.4, set it to $DOCUMENTUM as follows:\n#    export DOCUMENTUM_SHARED=$DOCUMENTUM\n# See Usage() for details;\n\nUsage() {\n   cat - &lt;&lt;EoU\n.\/widql docbase[@[host][:port]] [other standard parameters] [--verbose] [--append] [--keep]\n.\/wiapi docbase[@[host][:port]] [other standard parameters] [--verbose] [--append] [--keep]\n.\/wdmawk [-v] docbase[@[host][:port]] [dmawk -v parameters] [--verbose] [--append] [--keep]\nE.g.:\n   wiapi dmtest\nor:\n   widql dmtest@remote_host\nor:\n   widql dmtest@remote_host:1491 -Udmadmin -Pxxxx\nor:\n   wiapi dmtest@:1491 --append\nor:\n   wdmawk -v dmtest01@docker:5489 -f .\/twdmawk.awk -v ...\nor:\n   wdmawk dmtest01@docker:2489 -f .\/twdmawk.awk -v ...\nor:\n   wiapi dmtest@remote_host:1491 --append --keep\netc...\nIf --verbose is present, the changes applied to ($DFC_CONFIG | $DOCUMENTUM[_SHARED])\/config\/dfc.properties are displayed.\nIf --append is present, a new entry is appended to the dfc.properties file, the value couple dfc.docbroker.host and dfc.docbroker.port, and the existing ones are not commented out so they are still usable;\nIf --append is not present, all the entries are removed prior to inserting the new one;\nIf --keep is present, the changed dfc.properties file is not reverted to the changed one, i.e. the changes are made permanent;\nIf a change of configuration has been requested, the original config file is first saved with a timestamp appended and restored on return from the standard tools, unless --keep is present in which case\nthe backup file is also kept so it is still possible to manually revert to the original configuration;\nwdmawk invokes dmawk passing it the -v docbase=$docbase command-line parameter;\nIn addition, if -v docbase[@[host][:port]] is used, -v repo_target=docbase[@[host][:port]] is also passed to dmawk;\nInstead of a in-line target definition, environment variables can also be used, e.g.:\n   widql dmtest@$dmtestVM ...\nwhere $dmtestVM resolves to e.g. docker:1489\nor even:\n   widql $test01c ...\nwhere $test01c resolves to e.g. dmtest01@container01:1489\nAs the environment variable is resolved by the shell before it invokes the program, make sure it has a definition, e.g. source a configuration file;\nEoU\n   exit 0\n}\n\nif [[ $# -eq 0 ]]; then\n   Usage\nfi\n\n# wrapper's file name;\nPROGRAM_NAME=dctm-wrapper\n\n# has the wrapper been called directly or through one of its sym links ?\ndctm_program=$(basename $0)\nif [[ \"$dctm_program\" != $PROGRAM_NAME ]]; then\n   dctm_program=${dctm_program:1}\n   bWrapperCalled=0\nelse\n   bWrapperCalled=1\nfi\n\n# save command;\ncurrent_cmd=\"$0 $*\"\n\n# which original program shall possibly be called ?\ndctm_program=$(basename $0); dctm_program=${dctm_program:1}\nif [[ $dctm_program == \"dmawk\" ]]; then\n   bFordmawk=1 \nelse\n   bFordmawk=0 \nfi\n\n# look for the --verbose, --append or --keep options;\n# remove them from the command-line if found so they are not passed to the standard Documentum's tools;\n# the goal is to clean up the command-line from the enhancements options so it can be passed to the official tools;\nbVerbose=0\nbAppend=0\nbKeep=0\nposTarget=1\npassTarget2awk=0\nwhile true; do\n   index=-1\n   bChanged=0\n   for i in \"$@\"; do\n      (( index += 1 ))\n      if [[ \"$i\" == \"--verbose\" ]]; then\n         bVerbose=1\n         bChanged=1\n         break\n      elif [[ \"$i\" == \"--append\" ]]; then\n         bAppend=1\n         bChanged=1\n         break\n      elif [[ \"$i\" == \"--keep\" ]]; then\n         bKeep=1\n         bChanged=1\n         break\n      elif [[ \"$i\" == \"-v\" &amp;&amp; $bFordmawk -eq 1 &amp;&amp; $index -eq 0 ]]; then\n\t passTarget2awk=1\n         bChanged=1\n         break\n      fi\n   done\n   if [[ $bChanged -eq 1 ]]; then\n      set -- ${@:1:index} ${@:index+2:$#-index-1}\n   else\n      break\n   fi\ndone\n\n[[ bVerbose -eq 1 ]] &amp;&amp; echo \"current_cmd=[$current_cmd]\"\n\ntarget=$1\nremote_info=$(echo $1 | gawk '{\n   docbase = \"\"; hostname = \"\"; port = \"\"\n   if (match($0, \/@[^ t:]*\/)) {\n      docbase = substr($0, 1, RSTART - 1)\n      hostname = substr($0, RSTART + 1, RLENGTH - 1)\n      rest = substr($0, RSTART + RLENGTH)\n      if (1 == match(rest, \/:[0-9]+\/))\n         port = substr(rest, 2, RLENGTH - 1)\n   }\n   else docbase = $0\n}\nEND {\n   printf(\"%s:%s:%s\", docbase, hostname, port)\n}')\ndocbase=$(echo $remote_info | cut -d: -f1)\nhostname=$(echo $remote_info | cut -d: -f2)\nport=$(echo $remote_info | cut -d: -f3)\n\n# any modifications to the config file requested ?\nif [[ ! -z $hostname || ! -z $port ]]; then\n   # the dfc.properties file must be changed for the new target repository;\n   if [ ! -z $DFC_CONFIG ]; then\n      dfc_config=$DFC_CONFIG\n   else\n      dfc_config=$DOCUMENTUM_SHARED\/config\/dfc.properties\n   fi\n   if [[ ! -f $dfc_config ]]; then\n      echo \"$dfc_config not found\"\n      echo \"check the $DOCUMENTUM_SHARED environment variable\"\n      echo \" in &ge; 16.4, set it to $DOCUMENTUM\"\n      exit 1\n   fi\n   \n   # save the current config file;\n   # don't need if no modifications to be done;\n   if [[ $bWrapperCalled -eq 0 ]]; then\n      backup_file=${dfc_config}_saved_$(date +\"%Y-%m-%d_%H:%M:%S\")\n      cp $dfc_config ${backup_file}\n   fi\n\n   [[ $bVerbose -eq 1 ]] &amp;&amp; echo \"changing to $hostname:$port...\"\n   pid=$$; gawk -v hostname=\"$hostname\" -v port=\"$port\" -v bAppend=$bAppend -v bVerbose=$bVerbose -v bKeep=$bKeep -v pid=$$ 'BEGIN {\n      bFirst_hostname = 0; first_hostname = \"\"\n      bFirst_port     = 0 ;    first_port = \"\"\n      max_index = -1\n   }\n   {\n      if (match($0, \/^dfc.docbroker.host[[0-9]+]=\/)) {\n         if (!hostname &amp;&amp; !bFirst_hostname) {\n            # save the first host name to be used if command-line hostname was omitted;\n            bFirst_hostname = 1\n            first_hostname = substr($0, RLENGTH +1)\n         }\n         match($0, \/[[0-9]+]\/); index_number = substr($0, RSTART + 1, RLENGTH - 2)\n         if (bAppend) {\n            # leave the entry;\n            print $0\n            if (index_number &gt; max_index)\n               max_index = index_number\n         }\n         else {\n            # do not, which will remove the entry;\n            if (bVerbose)\n               print \"# removed:\", $0 &gt; (\"\/tmp\/tmp_\" pid)\n         }\n      }\n      else if (match($0, \/^dfc.docbroker.port[[0-9]+]=\/)) {\n         if (!port &amp;&amp; !bFirst_port) {\n            # save the first port to be used if command-line port was omitted;\n            bFirst_port = 1\n            first_port = substr($0, RLENGTH +1)\n         }\n         if (bAppend)\n            # leave the entry;\n            print $0\n         else {\n            # do nothing, which will remove the entry;\n            if (bVerbose)\n               print \"# removed:\", $0 &gt; (\"\/tmp\/tmp_\" pid)\n         }\n      }\n      else print\n   }\n   END {\n      if (!hostname)\n         hostname = first_hostname\n      if (!port)\n         port = first_port\n      if (bAppend)\n         index_number = max_index + 1\n      else\n         index_number = 0\n      print \"dfc.docbroker.host[\" index_number \"]=\" hostname\n      print \"dfc.docbroker.port[\" index_number \"]=\" port\n      if (bVerbose) {\n         print \"# added: dfc.docbroker.host[\" index_number \"]=\" hostname &gt; (\"\/tmp\/tmp_\" pid)\n         print \"# added: dfc.docbroker.port[\" index_number \"]=\" port     &gt; (\"\/tmp\/tmp_\" pid)\n      }\n      close(\"\/tmp\/tmp_\" pid)\n   }' $dfc_config &gt; \/tmp\/new_config_$$\n\n   if [[ $bVerbose -eq 1 ]]; then\n      echo \"requested changes:\"\n      cat \/tmp\/tmp_$$\n      rm \/tmp\/tmp_$$\n      echo \"diffs:\"\n      diff $dfc_config \/tmp\/new_config_$$\n   fi \n\n   if [[ $bWrapperCalled -eq 0 ]]; then\n      mv \/tmp\/new_config_$$ $dfc_config\n      if [[ $bVerbose -eq 1 ]]; then\n         echo \"$dfc_config to be used:\"\n         cat $dfc_config\n      fi\n   else\n      cat \/tmp\/new_config_$$\n      rm \/tmp\/new_config_$$\n   fi\n   shift\n\n   if [[ $bFordmawk -eq 1 ]]; then\n      docbase=\"-v docbase=$docbase\"\n      [[ $passTarget2awk -eq 1 ]] &amp;&amp; docbase=\"-v repo_target=$target $docbase\"\n   fi\n   [[ $bVerbose -eq 1 ]] &amp;&amp; echo \"calling original: $DM_HOME\/bin\/${dctm_program} $docbase $*\"\n   [[ $bWrapperCalled -eq 0 ]] &amp;&amp; $DM_HOME\/bin\/${dctm_program} $docbase $*\n\n   # restore original config file;\n   [[ $bWrapperCalled -eq 0 &amp;&amp; $bKeep -eq 0 ]] &amp;&amp; mv ${backup_file} $dfc_config\nelse\n   if [[ $bVerbose -eq 1 ]]; then\n      echo \"no change to current $dfc_config file\"\n      [[ $bWrapperCalled -eq 0 ]] &amp;&amp; echo \"calling original: $DM_HOME\/bin\/${dctm_program} $*\"\n   fi\n   [[ $bWrapperCalled -eq 0 ]] &amp;&amp; $DM_HOME\/bin\/${dctm_program} $*\nfi\n<\/pre>\n<p>The original configuration file is always saved on entry by appending a timestamp precise to the second which, unless you&#8217;re the Flash running the command twice in the background with the option &ndash;&ndash;keep but without &ndash;&ndash;append, should be enough to preserve the original content.<br \/>\nTo make the command-line parsing simpler, the script relies on the final invoked command for checking any syntax errors. Feel free to modify it and make it more robust if you need that. As said earlier, the &ndash;&ndash;verbose option can help troubleshooting unexpected results here.<br \/>\nSee <a title=\"Connecting to a Repository via a Dynamically Edited dfc.properties File (part II)\" href=\"https:\/\/www.dbi-services.com\/blog\/?p=32822&amp;preview=true\" target=\"_blank\" rel=\"noopener noreferrer\">part II of this article<\/a> for the tests.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Now that we have containerized content servers, it is very easy, maybe too easy, to create new repositories. Their creation is still not any faster (whether they are containerized or not is irrelevant here) but given a configuration file it just takes one command to instantiate an image into a running container with working repositories [&hellip;]<\/p>\n","protected":false},"author":40,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[525],"tags":[],"type_dbi":[],"class_list":["post-12490","post","type-post","status-publish","format-standard","hentry","category-enterprise-content-management"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.2 (Yoast SEO v27.2) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Connecting to a Repository via a Dynamically Edited dfc.properties File (part I) - dbi Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Connecting to a Repository via a Dynamically Edited dfc.properties File (part I)\" \/>\n<meta property=\"og:description\" content=\"Now that we have containerized content servers, it is very easy, maybe too easy, to create new repositories. Their creation is still not any faster (whether they are containerized or not is irrelevant here) but given a configuration file it just takes one command to instantiate an image into a running container with working repositories [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/\" \/>\n<meta property=\"og:site_name\" content=\"dbi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2019-06-16T16:36:24+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-10-24T07:32:14+00:00\" \/>\n<meta name=\"author\" content=\"Middleware Team\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Middleware Team\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"23 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/\"},\"author\":{\"name\":\"Middleware Team\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/8d8563acfc6e604cce6507f45bac0ea1\"},\"headline\":\"Connecting to a Repository via a Dynamically Edited dfc.properties File (part I)\",\"datePublished\":\"2019-06-16T16:36:24+00:00\",\"dateModified\":\"2025-10-24T07:32:14+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/\"},\"wordCount\":2798,\"commentCount\":0,\"articleSection\":[\"Enterprise content management\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/\",\"url\":\"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/\",\"name\":\"Connecting to a Repository via a Dynamically Edited dfc.properties File (part I) - dbi Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#website\"},\"datePublished\":\"2019-06-16T16:36:24+00:00\",\"dateModified\":\"2025-10-24T07:32:14+00:00\",\"author\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/8d8563acfc6e604cce6507f45bac0ea1\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\/\/www.dbi-services.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Connecting to a Repository via a Dynamically Edited dfc.properties File (part I)\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#website\",\"url\":\"https:\/\/www.dbi-services.com\/blog\/\",\"name\":\"dbi Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.dbi-services.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/8d8563acfc6e604cce6507f45bac0ea1\",\"name\":\"Middleware Team\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/secure.gravatar.com\/avatar\/ddcae7ba0f9d1a0e7ae707f0e689e4a9c95bb48ec49c8e6d9cc86d43f4121cb6?s=96&d=mm&r=g\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/ddcae7ba0f9d1a0e7ae707f0e689e4a9c95bb48ec49c8e6d9cc86d43f4121cb6?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/ddcae7ba0f9d1a0e7ae707f0e689e4a9c95bb48ec49c8e6d9cc86d43f4121cb6?s=96&d=mm&r=g\",\"caption\":\"Middleware Team\"},\"url\":\"https:\/\/www.dbi-services.com\/blog\/author\/middleware-team\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Connecting to a Repository via a Dynamically Edited dfc.properties File (part I) - dbi Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/","og_locale":"en_US","og_type":"article","og_title":"Connecting to a Repository via a Dynamically Edited dfc.properties File (part I)","og_description":"Now that we have containerized content servers, it is very easy, maybe too easy, to create new repositories. Their creation is still not any faster (whether they are containerized or not is irrelevant here) but given a configuration file it just takes one command to instantiate an image into a running container with working repositories [&hellip;]","og_url":"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/","og_site_name":"dbi Blog","article_published_time":"2019-06-16T16:36:24+00:00","article_modified_time":"2025-10-24T07:32:14+00:00","author":"Middleware Team","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Middleware Team","Est. reading time":"23 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/#article","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/"},"author":{"name":"Middleware Team","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/8d8563acfc6e604cce6507f45bac0ea1"},"headline":"Connecting to a Repository via a Dynamically Edited dfc.properties File (part I)","datePublished":"2019-06-16T16:36:24+00:00","dateModified":"2025-10-24T07:32:14+00:00","mainEntityOfPage":{"@id":"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/"},"wordCount":2798,"commentCount":0,"articleSection":["Enterprise content management"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/","url":"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/","name":"Connecting to a Repository via a Dynamically Edited dfc.properties File (part I) - dbi Blog","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/#website"},"datePublished":"2019-06-16T16:36:24+00:00","dateModified":"2025-10-24T07:32:14+00:00","author":{"@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/8d8563acfc6e604cce6507f45bac0ea1"},"breadcrumb":{"@id":"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.dbi-services.com\/blog\/connecting-to-a-repository-via-a-dynamically-edited-dfc-properties-file-part-i\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/www.dbi-services.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Connecting to a Repository via a Dynamically Edited dfc.properties File (part I)"}]},{"@type":"WebSite","@id":"https:\/\/www.dbi-services.com\/blog\/#website","url":"https:\/\/www.dbi-services.com\/blog\/","name":"dbi Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.dbi-services.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/8d8563acfc6e604cce6507f45bac0ea1","name":"Middleware Team","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/ddcae7ba0f9d1a0e7ae707f0e689e4a9c95bb48ec49c8e6d9cc86d43f4121cb6?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/ddcae7ba0f9d1a0e7ae707f0e689e4a9c95bb48ec49c8e6d9cc86d43f4121cb6?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/ddcae7ba0f9d1a0e7ae707f0e689e4a9c95bb48ec49c8e6d9cc86d43f4121cb6?s=96&d=mm&r=g","caption":"Middleware Team"},"url":"https:\/\/www.dbi-services.com\/blog\/author\/middleware-team\/"}]}},"_links":{"self":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/12490","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/users\/40"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/comments?post=12490"}],"version-history":[{"count":1,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/12490\/revisions"}],"predecessor-version":[{"id":41185,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/12490\/revisions\/41185"}],"wp:attachment":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/media?parent=12490"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/categories?post=12490"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/tags?post=12490"},{"taxonomy":"type","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/type_dbi?post=12490"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}